Much work on generalizing the notion of message "transport" and

allowing the use of annotations to customize transport for 
DObject fields and service/receiver methods.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5099 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Andrzej Kapolka
2008-05-16 01:47:33 +00:00
parent e279133868
commit ee74481cad
39 changed files with 864 additions and 123 deletions
@@ -46,6 +46,7 @@ import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
@@ -61,6 +62,9 @@ import com.samskivert.util.SortableArrayList;
import com.samskivert.util.StringUtil;
import com.samskivert.velocity.VelocityUtil;
import com.threerings.presents.annotation.TransportHint;
import com.threerings.presents.net.Transport;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.DSet;
import com.threerings.presents.dobj.OidList;
@@ -85,6 +89,11 @@ public class GenDObjectTask extends Task
{
_cloader = ClasspathUtils.getClassLoaderForPath(
getProject(), pathref);
// set the parent of the classloader to be the classloader used to load this task,
// rather than the classloader used to load Ant, so that we have access to Narya
// classes like TransportHint
((AntClassLoader)_cloader).setParent(getClass().getClassLoader());
}
/** Performs the actual work of the task. */
@@ -107,6 +116,7 @@ public class GenDObjectTask extends Task
_doclass = _cloader.loadClass(DObject.class.getName());
_dsclass = _cloader.loadClass(DSet.class.getName());
_olclass = _cloader.loadClass(OidList.class.getName());
} catch (Exception e) {
throw new BuildException("Can't resolve InvocationListener", e);
}
@@ -198,6 +208,18 @@ public class GenDObjectTask extends Task
ctx.put("capfield", StringUtil.unStudlyName(fname).toUpperCase());
ctx.put("upfield", StringUtils.capitalize(fname));
// determine the type of transport
TransportHint hint = f.getAnnotation(TransportHint.class);
String transport;
if (hint == null) {
transport = "DEFAULT";
} else {
transport = "getInstance(\n" +
" com.threerings.presents.net.Transport.Type." +
hint.type().name() + ", " + hint.channel() + ")";
}
ctx.put("transport", "com.threerings.presents.net.Transport." + transport);
// if this field is an array, we need its component types
if (ftype.isArray()) {
Class<?> etype = ftype.getComponentType();
@@ -38,6 +38,7 @@ import com.samskivert.util.StringUtil;
import com.threerings.presents.client.InvocationDecoder;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.net.Transport;
import com.threerings.presents.server.InvocationSender;
/**
@@ -85,7 +86,7 @@ public class GenReceiverTask extends InvocationTask
methods.add(new ServiceMethod(m, imports));
}
methods.sort();
// get rid of primitives and java.lang types
imports.removeGlobals();
@@ -106,6 +107,7 @@ public class GenReceiverTask extends InvocationTask
CollectionUtil.addAll(implist, imports);
checkedAdd(implist, ClientObject.class.getName());
checkedAdd(implist, InvocationSender.class.getName());
checkedAdd(implist, Transport.class.getName());
String dname = StringUtil.replace(rname, "Receiver", "Decoder");
checkedAdd(implist, rpackage + "." + dname);
implist.sort();
@@ -39,6 +39,7 @@ import com.threerings.presents.client.InvocationService;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.dobj.InvocationResponseEvent;
import com.threerings.presents.net.Transport;
import com.threerings.presents.server.InvocationDispatcher;
import com.threerings.presents.server.InvocationException;
import com.threerings.presents.server.InvocationProvider;
@@ -46,11 +47,11 @@ import com.threerings.presents.server.InvocationProvider;
/**
* An Ant task for generating invocation service marshalling and
* unmarshalling classes.
* TODO: when generating the imports for exported action script files, there are just enough
* TODO: when generating the imports for exported action script files, there are just enough
* conversions of primitive types (e.g. float -> Number), array types (e.g. int[] -> TypedArray)
* and three rings utility types (e.g. float -> Float) to make the existing serivces work. It
* should be possible to create a complete list of these conversions so that future services
* can be generated without problems.
* can be generated without problems.
*/
public class GenServiceTask extends InvocationTask
{
@@ -61,7 +62,7 @@ public class GenServiceTask extends InvocationTask
public ComparableArrayList<ServiceMethod> methods =
new ComparableArrayList<ServiceMethod>();
/** Contains all imports required for the parameters of the methods in this listener. */
public ImportSet imports = new ImportSet();
@@ -77,12 +78,12 @@ public class GenServiceTask extends InvocationTask
continue;
}
if (_verbose) {
System.out.println("Adding " + m + ", imports are " +
System.out.println("Adding " + m + ", imports are " +
StringUtil.toString(imports));
}
methods.add(new ServiceMethod(m, imports));
if (_verbose) {
System.out.println("Added " + m + ", imports are " +
System.out.println("Added " + m + ", imports are " +
StringUtil.toString(imports));
}
}
@@ -137,7 +138,7 @@ public class GenServiceTask extends InvocationTask
protected void processService (File source, Class service)
{
System.out.println("Processing " + service.getName() + "...");
// verify that the service class name is as we expect it to be
if (!service.getName().endsWith("Service")) {
System.err.println("Cannot process '" + service.getName() + "':");
@@ -167,7 +168,7 @@ public class GenServiceTask extends InvocationTask
sdesc.spackage, ".client", ".data");
// ----------- Part I - java marshaller
// start with all imports (service methods and listener methods)
ImportSet imports = sdesc.constructAllImports();
@@ -175,30 +176,31 @@ public class GenServiceTask extends InvocationTask
imports.add(sdesc.service);
imports.add(Client.class);
imports.add(InvocationMarshaller.class);
imports.add(Transport.class);
// if any listeners are to be present, they need the response event
if (sdesc.listeners.size() > 0) {
imports.add(InvocationResponseEvent.class);
}
// import classes contained in arrays
imports.translateClassArrays();
// get rid of java.lang stuff and primitives
imports.removeGlobals();
// get rid of all arrays (they are automatic in java)
imports.removeArrays();
// for each listener type, also import the corresponding marshaller
imports.duplicateAndMunge("*Listener",
imports.duplicateAndMunge("*Listener",
"Service", "Marshaller",
"Listener", "Marshaller",
".client.", ".data.");
// import the parent class of Foo$Bar
imports.swapInnerClassesForParents();
// remove imports in our own package
imports.removeSamePackage(mpackage);
@@ -234,15 +236,15 @@ public class GenServiceTask extends InvocationTask
// start with the service method imports
imports = sdesc.imports.clone();
// add some things that marshallers just need
imports.add(sdesc.service);
imports.add(Client.class);
imports.add(InvocationMarshaller.class);
// replace inner classes with action script equivalents
imports.translateInnerClasses();
// replace primitive types with OOO types (required for unboxing)
imports.replace("byte", "com.threerings.util.Byte");
imports.replace("int", "com.threerings.util.Integer");
@@ -268,7 +270,7 @@ public class GenServiceTask extends InvocationTask
// get rid of java.lang stuff and any remaining primitives
imports.removeGlobals();
if (imports.removeAll("[L*") > 0) {
imports.add("com.threerings.io.TypedArray");
}
@@ -304,20 +306,20 @@ public class GenServiceTask extends InvocationTask
// start imports with just those used by listener methods
imports = listener.imports.clone();
// always need the super class and the listener class
imports.add(imlm);
imports.add(listener.listener);
// replace '$' with '_' for action script naming convention
imports.translateInnerClasses();
// convert primitive java types to ooo util types
imports.replace("long", "com.threerings.util.Long");
// get rid of remaining primitives and java.lang types
imports.removeGlobals();
// remove imports in our own package
imports.removeSamePackage(mpackage);
@@ -359,7 +361,7 @@ public class GenServiceTask extends InvocationTask
// get rid of primitives and java.lang classes
imports.removeGlobals();
// get rid of remaining arrays
imports.removeArrays();
@@ -394,20 +396,20 @@ public class GenServiceTask extends InvocationTask
// start with just the imports needed by listener methods
imports = listener.imports.clone();
// add things needed by all listeners
imports.add(isil);
imports.add(listener.listener);
// change Foo$Bar to Foo_Bar
imports.translateInnerClasses();
// convert java primitive types to ooo util types
// convert java primitive types to ooo util types
imports.replace("long", "com.threerings.util.Long");
// get rid of remaining primitives and java.lang types
imports.removeGlobals();
// remove imports in our own package
imports.removeSamePackage(sdesc.spackage);
@@ -450,29 +452,29 @@ public class GenServiceTask extends InvocationTask
// swap Client for ClientObject
imports.add(ClientObject.class);
imports.remove(Client.class);
// add some classes required for all dispatchers
imports.add(InvocationMarshaller.class);
imports.add(InvocationDispatcher.class);
imports.add(InvocationException.class);
// import classes contained in arrays
imports.translateClassArrays();
// get rid of primitives and java.lang types
imports.removeGlobals();
// get rid of arrays
imports.removeArrays();
// import the Marshaller corresponding to the service
imports.addMunged(sdesc.service,
imports.addMunged(sdesc.service,
"Service", "Marshaller",
".client.", ".data.");
// import Foo instead of Foo$Bar
imports.swapInnerClassesForParents();
// remove imports in our own package
imports.removeSamePackage(dpackage);
@@ -504,7 +506,7 @@ public class GenServiceTask extends InvocationTask
if (_verbose) {
System.out.println("Generating provider");
}
String name = StringUtil.replace(sdesc.sname, "Service", "");
String mpackage = StringUtil.replace(
sdesc.spackage, ".client", ".server");
@@ -515,24 +517,24 @@ public class GenServiceTask extends InvocationTask
// swap Client for ClientObject
imports.add(ClientObject.class);
imports.remove(Client.class);
// import superclass
imports.add(InvocationProvider.class);
// any method that takes a listener may throw this
// any method that takes a listener may throw this
if (sdesc.hasAnyListenerArgs()) {
imports.add(InvocationException.class);
}
// import classes contained in arrays
imports.translateClassArrays();
// get rid of primitives and java.lang types
imports.removeGlobals();
// get rid of arrays
imports.removeArrays();
// import Foo instead of Foo$Bar
imports.swapInnerClassesForParents();
@@ -593,19 +595,19 @@ public class GenServiceTask extends InvocationTask
}
}
if (_verbose) {
System.out.println("Adding " + m + ", imports are " +
System.out.println("Adding " + m + ", imports are " +
StringUtil.toString(imports));
}
methods.add(new ServiceMethod(m, imports));
if (_verbose) {
System.out.println("Added " + m + ", imports are " +
System.out.println("Added " + m + ", imports are " +
StringUtil.toString(imports));
}
}
listeners.sort();
methods.sort();
}
/**
* Checks if any of the service method arguments are listener types.
*/
@@ -640,7 +642,7 @@ public class GenServiceTask extends InvocationTask
ComparableArrayList<ServiceListener> listeners =
new ComparableArrayList<ServiceListener>();
}
/** The path to our ActionScript source files. */
protected File _asroot;
@@ -34,6 +34,7 @@ import java.util.List;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
@@ -46,6 +47,9 @@ import org.apache.velocity.app.VelocityEngine;
import com.samskivert.util.StringUtil;
import com.samskivert.velocity.VelocityUtil;
import com.threerings.presents.annotation.TransportHint;
import com.threerings.presents.net.Transport;
import com.threerings.presents.client.InvocationService.InvocationListener;
/**
@@ -90,7 +94,7 @@ public abstract class InvocationTask extends Task
}
}
/** Used to keep track of invocation service methods or listener
/** Used to keep track of invocation service methods or listener
* methods. */
public class ServiceMethod implements Comparable<ServiceMethod>
{
@@ -101,13 +105,13 @@ public abstract class InvocationTask extends Task
/**
* Creates a new service method.
* @param method the method to inspect
* @param method the method to inspect
* @param imports will be filled with the types required by the method
*/
public ServiceMethod (Method method, ImportSet imports)
{
this.method = method;
// we need to look through our arguments and note any needed
// imports in the supplied table
Class<?>[] args = method.getParameterTypes();
@@ -249,6 +253,16 @@ public abstract class InvocationTask extends Task
return buf.toString();
}
public String getTransport ()
{
TransportHint hint = method.getAnnotation(TransportHint.class);
if (hint == null) {
return "Transport.DEFAULT";
}
return "Transport.getInstance(Transport.Type." +
hint.type().name() + ", " + hint.channel() + ")";
}
protected String boxArgument (Class<?> clazz, int index)
{
if (_ilistener.isAssignableFrom(clazz)) {
@@ -306,6 +320,11 @@ public abstract class InvocationTask extends Task
{
_cloader = ClasspathUtils.getClassLoaderForPath(
getProject(), pathref);
// set the parent of the classloader to be the classloader used to load this task,
// rather than the classloader used to load Ant, so that we have access to Narya
// classes like TransportHint
((AntClassLoader)_cloader).setParent(getClass().getClassLoader());
}
/** Performs the actual work of the task. */
@@ -405,7 +424,7 @@ public abstract class InvocationTask extends Task
/** Show extra output if set. */
protected boolean _verbose;
/** Used to do our own classpath business. */
protected ClassLoader _cloader;
@@ -10,7 +10,8 @@
{
$type ovalue = this.$field;
requestAttributeChange(
$capfield, $wrapfield, $wrapofield);
$capfield, $wrapfield, $wrapofield,
$transport);
this.$field = $clonefield;
}
#if ($elemtype)
@@ -28,7 +29,8 @@
{
$elemtype ovalue = this.$field[index];
requestElementUpdate(
$capfield, index, $wrapelem, $wrapoelem);
$capfield, index, $wrapelem, $wrapoelem,
$transport);
this.$field[index] = value;
}
#end
@@ -25,7 +25,9 @@
*/
public void update$upfield ($etype elem)
{
requestEntryUpdate($capfield, $field, elem);
requestEntryUpdate(
$capfield, $field, elem,
$transport);
}
/**
@@ -32,7 +32,7 @@ public class ${name}Marshaller extends InvocationMarshaller
_invId = null;
omgr.postEvent(new InvocationResponseEvent(
callerOid, requestId, $lm.code,
new Object[] { $lm.getWrappedArgList(false) }));
new Object[] { $lm.getWrappedArgList(false) }, transport));
}
#end
@@ -71,7 +71,7 @@ public class ${name}Marshaller extends InvocationMarshaller
#end
sendRequest(arg1, $m.code, new Object[] {
$m.getWrappedArgList(true)
});
}, $m.transport);
}
#end
}
@@ -20,7 +20,7 @@ public class ${name}Sender extends InvocationSender
{
sendNotification(
target, ${name}Decoder.RECEIVER_CODE, ${name}Decoder.$m.code,
new Object[] { $m.getWrappedArgList(false) });
new Object[] { $m.getWrappedArgList(false) }, $m.transport);
}
#end