36a98f8794
calls on the Java side, to match this added convenience we introduced to the ActionScript side some time ago. Before arms are raised into the air and waved like they really do care, bear the following in mind: Nothing will change at all on your project until you take some action to do so. That action could include any of the following: 1. Continue to use narya-tools-1.2 to run genservice and you'll get the old style "pass a Client reference for every service method invocation" marshallers and you can continue to live happily in the past. 2. Leave some of your stuff un-regenerated, but generate new stuff using the new tools and use the Client-reference-free style for new services. This is likely to engender some PITA if you're trying to use both styles in the same project because genservice just wants to operate on everything, but I offer it as an option in case some other PITA motivates the tolerance of this (presumably smaller) PITA. 3. Embrace the brave new world of simplicity and wield your good friend sed for fun and profit: (make necessary build.xml updates to start using narya-tools-1.4-SNAPSHOT) % find src -name '*Service.java' | xargs sed -i 's:Client client, ::g' (this will remove the Client argument from most of your services, some manual fixes may be needed) % ant genservice % find src -name '*.java' | xargs sed -i 's:_ctx.getClient(), ::g' (this will fix most of the places that pass a client reference into an invocation service, some manual fixes will probably be needed) I did this on Vilya and it magically took care of like 95% of the places where changes were needed. The remaining half dozen changes were painless. Clearly something like Yohoho vastly dwarfs Vilya in scope, but someone looking for an hour or two of mindless typing to distract them on a lazy Sunday afternoon would likely breeze through even that vast codebase without breaking a sweat. Two more notes: 1. I'm going to switch Narya and Vilya over to the new style, and I'll be fixing all the projects that use their services. 2. I lied (a teeny bit) about nothing changing. I made the old Client-taking sendRequest methods deprecated, so projects that don't switch to the new style will be presented with a large number of deprecation warnings. If this is too terrible a burden to bear, I can remove the deprecation annotations on those methods. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6395 542714f4-19e9-0310-aa3c-eee0fc999fb1
378 lines
14 KiB
Java
378 lines
14 KiB
Java
//
|
|
// $Id$
|
|
//
|
|
// Narya library - tools for developing networked games
|
|
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
|
// http://code.google.com/p/narya/
|
|
//
|
|
// This library is free software; you can redistribute it and/or modify it
|
|
// under the terms of the GNU Lesser General Public License as published
|
|
// by the Free Software Foundation; either version 2.1 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
package com.threerings.presents.tools;
|
|
|
|
import java.lang.reflect.GenericArrayType;
|
|
import java.lang.reflect.Method;
|
|
import java.lang.reflect.ParameterizedType;
|
|
import java.lang.reflect.Type;
|
|
import java.lang.reflect.WildcardType;
|
|
|
|
import java.io.File;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
|
|
import com.google.common.base.Predicate;
|
|
import com.google.common.collect.Iterables;
|
|
import com.google.common.collect.Lists;
|
|
|
|
import com.samskivert.util.Logger;
|
|
import com.samskivert.util.StringUtil;
|
|
|
|
import com.threerings.presents.annotation.TransportHint;
|
|
import com.threerings.presents.client.Client;
|
|
import com.threerings.presents.client.InvocationService.InvocationListener;
|
|
import com.threerings.presents.net.Transport;
|
|
|
|
/**
|
|
* A base Ant task for generating invocation service related marshalling and unmarshalling
|
|
* classes.
|
|
*/
|
|
public abstract class InvocationTask extends GenTask
|
|
{
|
|
/** Used to keep track of invocation service method listener arguments. */
|
|
public class ListenerArgument
|
|
{
|
|
public Class<?> listener;
|
|
|
|
public ListenerArgument (int index, Class<?> listener) {
|
|
this.listener = listener;
|
|
_index = index;
|
|
}
|
|
|
|
public String getMarshaller () {
|
|
String name = GenUtil.simpleName(listener);
|
|
// handle ye olde special case
|
|
if (name.equals("InvocationService.InvocationListener")) {
|
|
return "ListenerMarshaller";
|
|
}
|
|
name = name.replace("Service", "Marshaller");
|
|
return name.replace("Listener", "Marshaller");
|
|
}
|
|
|
|
public String getActionScriptMarshaller () {
|
|
// handle ye olde special case
|
|
String name = listener.getName();
|
|
if (name.endsWith("InvocationService$InvocationListener")) {
|
|
return "InvocationMarshaller_ListenerMarshaller";
|
|
} else {
|
|
return getMarshaller().replace('.', '_');
|
|
}
|
|
}
|
|
|
|
public int getIndex () {
|
|
return _index+1;
|
|
}
|
|
|
|
protected int _index;
|
|
}
|
|
|
|
/** Used to keep track of invocation service methods or listener methods. */
|
|
public class ServiceMethod implements Comparable<ServiceMethod>
|
|
{
|
|
public Method method;
|
|
public List<ListenerArgument> listenerArgs = Lists.newArrayList();
|
|
|
|
/**
|
|
* Creates a new service method.
|
|
* @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;
|
|
|
|
// if this method has listener arguments, we need to add listener argument info for them
|
|
Class<?>[] args = method.getParameterTypes();
|
|
for (int ii = 0; ii < args.length; ii++) {
|
|
Class<?> arg = args[ii];
|
|
while (arg.isArray()) {
|
|
arg = arg.getComponentType();
|
|
}
|
|
|
|
if (_ilistener.isAssignableFrom(arg)) {
|
|
listenerArgs.add(new ListenerArgument(ii, arg));
|
|
}
|
|
}
|
|
|
|
// we need to look through our arguments and note any needed imports in the supplied
|
|
// table
|
|
for (Type type : method.getGenericParameterTypes()) {
|
|
addImportsForType(type, imports);
|
|
}
|
|
|
|
// import Transport if used
|
|
if (!StringUtil.isBlank(getTransport())) {
|
|
imports.add(Transport.class);
|
|
}
|
|
}
|
|
|
|
public String getCode () {
|
|
return StringUtil.unStudlyName(method.getName()).toUpperCase();
|
|
}
|
|
|
|
public String getSenderMethodName () {
|
|
String mname = method.getName();
|
|
if (mname.startsWith("received")) {
|
|
return "send" + mname.substring("received".length());
|
|
} else {
|
|
return mname;
|
|
}
|
|
}
|
|
|
|
public String getArgList () {
|
|
StringBuilder buf = new StringBuilder();
|
|
Type[] ptypes = method.getGenericParameterTypes();
|
|
for (int ii = 0; ii < ptypes.length; ii++) {
|
|
if (buf.length() > 0) {
|
|
buf.append(", ");
|
|
}
|
|
String simpleName = GenUtil.simpleName(ptypes[ii]);
|
|
if (method.isVarArgs() && ii == ptypes.length - 1) {
|
|
// Switch [] with ... for varargs
|
|
buf.append(simpleName.substring(0, simpleName.length() - 2)).append("...");
|
|
} else {
|
|
buf.append(simpleName);
|
|
}
|
|
buf.append(" arg").append(ii+1);
|
|
}
|
|
return buf.toString();
|
|
}
|
|
|
|
public String getASArgList () {
|
|
StringBuilder buf = new StringBuilder();
|
|
Class<?>[] args = method.getParameterTypes();
|
|
for (int ii = 0; ii < args.length; ii++) {
|
|
if (buf.length() > 0) {
|
|
buf.append(", ");
|
|
}
|
|
buf.append("arg").append(ii+1).append(" :");
|
|
buf.append(GenUtil.simpleASName(args[ii]));
|
|
}
|
|
return buf.toString();
|
|
}
|
|
|
|
public String getASInvokArgList () {
|
|
StringBuilder buf = new StringBuilder();
|
|
Class<?>[] args = method.getParameterTypes();
|
|
for (int ii = 0; ii < args.length; ii++) {
|
|
if (buf.length() > 0) {
|
|
buf.append(", ");
|
|
}
|
|
buf.append("arg").append(ii);
|
|
}
|
|
return buf.toString();
|
|
}
|
|
|
|
public String getWrappedArgList () {
|
|
StringBuilder buf = new StringBuilder();
|
|
Class<?>[] args = method.getParameterTypes();
|
|
for (int ii = 0; ii < args.length; ii++) {
|
|
if (buf.length() > 0) {
|
|
buf.append(", ");
|
|
}
|
|
buf.append(boxArgument(args[ii], ii+1));
|
|
}
|
|
return buf.toString();
|
|
}
|
|
|
|
public String getASWrappedArgList () {
|
|
StringBuilder buf = new StringBuilder();
|
|
Class<?>[] args = method.getParameterTypes();
|
|
for (int ii = 0; ii < args.length; ii++) {
|
|
if (buf.length() > 0) {
|
|
buf.append(", ");
|
|
}
|
|
String index = String.valueOf(ii+1);
|
|
String arg;
|
|
if (_ilistener.isAssignableFrom(args[ii])) {
|
|
arg = GenUtil.boxASArgument(args[ii], "listener" + index);
|
|
} else {
|
|
arg = GenUtil.boxASArgument(args[ii], "arg" + index);
|
|
}
|
|
buf.append(arg);
|
|
}
|
|
return buf.toString();
|
|
}
|
|
|
|
public boolean hasArgs () {
|
|
return method.getParameterTypes().length > 0;
|
|
}
|
|
|
|
public boolean hasParameterizedArgs () {
|
|
return Iterables.any(
|
|
Arrays.asList(method.getGenericParameterTypes()), new Predicate<Type>() {
|
|
public boolean apply (Type type) {
|
|
// TODO: might eventually need to handle generic arrays and wildcard types
|
|
return (type instanceof ParameterizedType);
|
|
}
|
|
});
|
|
}
|
|
|
|
public String getUnwrappedArgListAsListeners () {
|
|
return getUnwrappedArgList(true);
|
|
}
|
|
|
|
public String getUnwrappedArgList () {
|
|
return getUnwrappedArgList(false);
|
|
}
|
|
|
|
public String getUnwrappedArgList (boolean listenerMode) {
|
|
StringBuilder buf = new StringBuilder();
|
|
Type[] ptypes = method.getGenericParameterTypes();
|
|
for (int ii = 0; ii < ptypes.length; ii++) {
|
|
if (buf.length() > 0) {
|
|
buf.append(", ");
|
|
}
|
|
buf.append(unboxArgument(ptypes[ii], ii, listenerMode));
|
|
}
|
|
return buf.toString();
|
|
}
|
|
|
|
public String getASUnwrappedArgListAsListeners () {
|
|
return getASUnwrappedArgList(true);
|
|
}
|
|
|
|
public String getASUnwrappedArgList () {
|
|
return getASUnwrappedArgList(false);
|
|
}
|
|
|
|
public String getASUnwrappedArgList (boolean listenerMode) {
|
|
StringBuilder buf = new StringBuilder();
|
|
Class<?>[] args = method.getParameterTypes();
|
|
for (int ii = 0; ii < args.length; ii++) {
|
|
if (buf.length() > 0) {
|
|
buf.append(", ");
|
|
}
|
|
String arg;
|
|
int argidx = ii;
|
|
if (listenerMode && _ilistener.isAssignableFrom(args[ii])) {
|
|
arg = "listener" + argidx;
|
|
} else {
|
|
arg = GenUtil.unboxASArgument(args[ii], "args[" + argidx + "]");
|
|
}
|
|
buf.append(arg);
|
|
}
|
|
return buf.toString();
|
|
}
|
|
|
|
public String getTransport () {
|
|
TransportHint hint = method.getAnnotation(TransportHint.class);
|
|
if (hint == null) {
|
|
// inherit hint from interface annotation
|
|
hint = method.getDeclaringClass().getAnnotation(TransportHint.class);
|
|
}
|
|
if (hint == null) {
|
|
return "";
|
|
}
|
|
return ", Transport.getInstance(Transport.Type." +
|
|
hint.type().name() + ", " + hint.channel() + ")";
|
|
}
|
|
|
|
// from interface Comparator<ServiceMethod>
|
|
public int compareTo (ServiceMethod other) {
|
|
return getCode().compareTo(other.getCode());
|
|
}
|
|
|
|
@Override // from Object
|
|
public boolean equals (Object other) {
|
|
return (other instanceof ServiceMethod) && compareTo((ServiceMethod)other) == 0;
|
|
}
|
|
|
|
@Override // from Object
|
|
public int hashCode () {
|
|
return getCode().hashCode();
|
|
}
|
|
|
|
protected void addImportsForType (Type type, ImportSet imports) {
|
|
if (type instanceof Class<?>) {
|
|
imports.add((Class<?>)type);
|
|
} else if (type instanceof ParameterizedType) {
|
|
imports.add((Class<?>)((ParameterizedType)type).getRawType());
|
|
for (Type param : ((ParameterizedType)type).getActualTypeArguments()) {
|
|
addImportsForType(param, imports);
|
|
}
|
|
} else if (type instanceof WildcardType) {
|
|
for (Type upper : ((WildcardType)type).getUpperBounds()) {
|
|
addImportsForType(upper, imports);
|
|
}
|
|
for (Type lower : ((WildcardType)type).getLowerBounds()) {
|
|
addImportsForType(lower, imports);
|
|
}
|
|
} else if (type instanceof GenericArrayType) {
|
|
addImportsForType(((GenericArrayType)type).getGenericComponentType(), imports);
|
|
} else {
|
|
System.err.println(Logger.format(
|
|
"Unhandled Type in adding imports for a service", "type", type, "typeClass",
|
|
type.getClass()));
|
|
}
|
|
}
|
|
|
|
protected String boxArgument (Class<?> clazz, int index) {
|
|
if (_ilistener.isAssignableFrom(clazz)) {
|
|
return GenUtil.boxArgument(clazz, "listener" + index);
|
|
} else {
|
|
return GenUtil.boxArgument(clazz, "arg" + index);
|
|
}
|
|
}
|
|
|
|
protected String unboxArgument (Type type, int index, boolean listenerMode) {
|
|
if (listenerMode && (type instanceof Class<?>) &&
|
|
_ilistener.isAssignableFrom((Class<?>)type)) {
|
|
return "listener" + index;
|
|
} else {
|
|
return GenUtil.unboxArgument(type, "args[" + index + "]");
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void execute ()
|
|
{
|
|
// resolve the InvocationListener and Client classes using our classloader
|
|
_ilistener = loadClass(InvocationListener.class.getName());
|
|
_iclient = loadClass(Client.class.getName());
|
|
|
|
super.execute();
|
|
}
|
|
|
|
protected static <T> void checkedAdd (List<T> list, T value)
|
|
{
|
|
if (!list.contains(value)) {
|
|
list.add(value);
|
|
}
|
|
}
|
|
|
|
protected static String replacePath (String source, String oldstr, String newstr)
|
|
{
|
|
return source.replace(oldstr.replace('/', File.separatorChar),
|
|
newstr.replace('/', File.separatorChar));
|
|
}
|
|
|
|
/** {@link InvocationListener} resolved with the proper classloader so that we can compare it
|
|
* to loaded derived classes. */
|
|
protected Class<?> _ilistener;
|
|
|
|
/** {@link Client} resolved with the proper classloader so that we can compare it to loaded
|
|
* derived classes. */
|
|
protected Class<?> _iclient;
|
|
}
|