package org.jboss.seam.remoting.gwt;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jboss.seam.core.ConversationPropagation;
import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import org.jboss.seam.servlet.ContextualHttpServletRequest;
import org.jboss.seam.web.AbstractResource;
import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.RPCRequest;
import com.google.gwt.user.server.rpc.RPCServletUtils;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.gwt.user.server.rpc.SerializationPolicy;
import com.google.gwt.user.server.rpc.SerializationPolicyLoader;
import com.google.gwt.user.server.rpc.SerializationPolicyProvider;
import com.google.gwt.user.server.rpc.UnexpectedException;
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter;
/**
* Abstract base class for GWT 1.5 integration.
*
* @author Shane Bryzak
*/
public abstract class GWTService extends AbstractResource implements SerializationPolicyProvider
{
protected static final LogProvider log = Logging.getLogProvider(GWTService.class);
private static final HashMap<String, Class<?>> TYPE_NAMES;
static
{
TYPE_NAMES = new HashMap<String, Class<?>>();
TYPE_NAMES.put("Z", boolean.class);
TYPE_NAMES.put("B", byte.class);
TYPE_NAMES.put("C", char.class);
TYPE_NAMES.put("D", double.class);
TYPE_NAMES.put("F", float.class);
TYPE_NAMES.put("I", int.class);
TYPE_NAMES.put("J", long.class);
TYPE_NAMES.put("S", short.class);
}
/**
* A cache of moduleBaseURL and serialization policy strong name to
* {@link SerializationPolicy}.
*/
private final Map<String, SerializationPolicy> serializationPolicyCache = new HashMap<String, SerializationPolicy>();
@Override
public String getResourcePath()
{
return "/gwt";
}
protected abstract ServerSerializationStreamReader getStreamReader();
protected abstract ServerSerializationStreamWriter getStreamWriter();
protected abstract String createResponse(
ServerSerializationStreamWriter stream, Class responseType,
Object responseObj, boolean isException);
// private final Set knownImplementedInterfaces = new HashSet();
private final ThreadLocal<HttpServletRequest> perThreadRequest = new ThreadLocal<HttpServletRequest>();
private final ThreadLocal<HttpServletResponse> perThreadResponse = new ThreadLocal<HttpServletResponse>();
/**
* This is called internally.
*
* @see RemoteServiceServlet#doPost
*/
@Override
public final void getResource(final HttpServletRequest request,
final HttpServletResponse response) throws ServletException,
IOException
{
try
{
// Store the request & response objects in thread-local storage.
perThreadRequest.set(request);
perThreadResponse.set(response);
new ContextualHttpServletRequest(request) {
@Override
public void process() throws Exception
{
try
{
// Read the request fully.
//
String requestPayload = RemoteServiceServlet_readContent(request);
RemoteServiceServlet_onBeforeRequestDeserialized(requestPayload);
// Invoke the core dispatching logic, which returns the
// serialized result
String responsePayload = processCall(requestPayload);
RemoteServiceServlet_onAfterResponseSerialized(responsePayload);
// Write the response.
//
RemoteServiceServlet_writeResponse(request, response,
responsePayload);
} catch (Throwable e)
{
RemoteServiceServlet_doUnexpectedFailure(e);
}
}
@Override
protected void restoreConversationId()
{
ConversationPropagation.instance().setConversationId(
GWTService.this.perThreadRequest.get().getParameter(
"conversationId"));
}
@Override
protected void handleConversationPropagation()
{
}
}.run();
} finally
{
perThreadRequest.remove();
perThreadResponse.remove();
}
}
/**
* This is public so that it can be unit tested easily without HTTP.
*/
public String processCall(String payload) throws SerializationException
{
// Create a stream to deserialize the request.
//
// ServerSerializationStreamReader streamReader = getStreamReader();
// streamReader.prepareToRead(payload);
//
// // Read the service interface
// //
// String serviceIntfName = streamReader.readString();
//
// // Read the method name.
// //
// String methodName = streamReader.readString();
//
// // Read the number and names of the parameter classes from the stream.
// // We have to do this so that we can find the correct overload of the
// // method.
// //
// int paramCount = streamReader.readInt();
// Class[] paramTypes = new Class[paramCount];
// for (int i = 0; i < paramTypes.length; i++)
// {
// String paramClassName = streamReader.readString();
// try
// {
// paramTypes[i] = getClassOrPrimitiveFromName(paramClassName);
// } catch (ClassNotFoundException e)
// {
// throw new SerializationException("Unknown parameter " + i
// + " type '" + paramClassName + "'", e);
// }
// }
//
// // Deserialize the parameters.
// //
// Object[] args = new Object[paramCount];
// for (int i = 0; i < args.length; i++)
// {
// args[i] = streamReader.deserializeValue(paramTypes[i]);
// }
try
{
SeamRPCRequest rpcRequest = RPC_decodeRequest(payload,
this.getClass(), this);
return RPC_invokeAndEncodeResponse(this, rpcRequest.getMethod(),
rpcRequest.getParameterTypes(), rpcRequest.getParameters(),
rpcRequest.getSerializationPolicy());
} catch (IncompatibleRemoteServiceException ex)
{
getServletContext()
.log(
"An IncompatibleRemoteServiceException was thrown while processing this call.",
ex);
return RPC.encodeResponseForFailure(null, ex);
}
// Make the call via reflection.
//
// String responsePayload = GENERIC_FAILURE_MSG;
// ServerSerializationStreamWriter streamWriter = getStreamWriter();
// Throwable caught = null;
// try
// {
// GWTToSeamAdapter.ReturnedObject returnedObject =
// adapter.callWebRemoteMethod(
// serviceIntfName, methodName, paramTypes, args);
// Class returnType = returnedObject.returnType;
// Object returnVal = returnedObject.returnedObject;
// // Class returnType = serviceIntfMethod.getReturnType();
// // Object returnVal = serviceIntfMethod.invoke(this, args);
// responsePayload = createResponse(streamWriter, returnType, returnVal,
// false);
// } catch (IllegalArgumentException e)
// {
// caught = e;
// } catch (IllegalAccessException e)
// {
// caught = e;
// } catch (InvocationTargetException e)
// {
// // Try to serialize the caught exception if the client is expecting it,
// // otherwise log the exception server-side.
// caught = e;
// Throwable cause = e.getCause();
// if (cause != null)
// {
// // Update the caught exception to the underlying cause
// caught = cause;
// // Serialize the exception back to the client if it's a declared
// // exception
// if (cause instanceof SerializableException)
// {
// Class thrownClass = cause.getClass();
// responsePayload = createResponse(streamWriter, thrownClass,
// cause, true);
// // Don't log the exception on the server
// caught = null;
// }
// }
// }
//
// if (caught != null)
// {
// responsePayload = GENERIC_FAILURE_MSG;
// ServletContext servletContext = getServletContext();
// // servletContext may be null (for example, when unit testing)
// if (servletContext != null)
// {
// // Log the exception server side
// servletContext.log("Exception while dispatching incoming RPC call",
// caught);
// }
// }
}
/**
* Gets the <code>HttpServletRequest</code> object for the current call. It
* is stored thread-locally so that simultaneous invocations can have
* different request objects.
*/
protected final HttpServletRequest getThreadLocalRequest()
{
return perThreadRequest.get();
}
/**
* Gets the <code>HttpServletResponse</code> object for the current call. It
* is stored thread-locally so that simultaneous invocations can have
* different response objects.
*/
protected final HttpServletResponse getThreadLocalResponse()
{
return perThreadResponse.get();
}
/**
* Returns an {@link RPCRequest} that is built by decoding the contents of an
* encoded RPC request and optionally validating that type can handle the
* request. If the type parameter is not <code>null</code>, the
* implementation checks that the type is assignable to the
* {@link com.google.gwt.user.client.rpc.RemoteService} interface requested
* in the encoded request string.
*
* <p>
* If the serializationPolicyProvider parameter is not <code>null</code>, it
* is asked for a {@link SerializationPolicy} to use to restrict the set of
* types that can be decoded from the request. If this parameter is
* <code>null</code>, then only subtypes of
* {@link com.google.gwt.user.client.rpc.IsSerializable IsSerializable} or
* types which have custom field serializers can be decoded.
* </p>
*
* <p>
* Invoking this method with <code>null</code> for the type parameter,
* <code>decodeRequest(encodedRequest, null)</code>, is equivalent to calling
* <code>decodeRequest(encodedRequest)</code>.
* </p>
*
* @param encodedRequest
* a string that encodes the
* {@link com.google.gwt.user.client.rpc.RemoteService} interface,
* the service method, and the arguments to pass to the service
* method
* @param type
* if not <code>null</code>, the implementation checks that the
* type is assignable to the
* {@link com.google.gwt.user.client.rpc.RemoteService} interface
* encoded in the encoded request string.
* @param serializationPolicyProvider
* if not <code>null</code>, the implementation asks this provider
* for a {@link SerializationPolicy} which will be used to restrict
* the set of types that can be decoded from this request
* @return an {@link RPCRequest} instance
*
* @throws NullPointerException
* if the encodedRequest is <code>null</code>
* @throws IllegalArgumentException
* if the encodedRequest is an empty string
* @throws IncompatibleRemoteServiceException
* if any of the following conditions apply:
* <ul>
* <li>if the types in the encoded request cannot be deserialized</li>
* <li>if the {@link ClassLoader} acquired from
* <code>Thread.currentThread().getContextClassLoader()</code>
* cannot load the service interface or any of the types specified
* in the encodedRequest</li>
* <li>the requested interface is not assignable to
* {@link com.google.gwt.user.client.rpc.RemoteService}</li>
* <li>the service method requested in the encodedRequest is not a
* member of the requested service interface</li>
* <li>the type parameter is not <code>null</code> and is not
* assignable to the requested
* {@link com.google.gwt.user.client.rpc.RemoteService} interface
* </ul>
*/
public static SeamRPCRequest RPC_decodeRequest(String encodedRequest,
Class<?> type, SerializationPolicyProvider serializationPolicyProvider)
{
if (encodedRequest == null)
{
throw new NullPointerException("encodedRequest cannot be null");
}
if (encodedRequest.length() == 0)
{
throw new IllegalArgumentException("encodedRequest cannot be empty");
}
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try
{
ServerSerializationStreamReader streamReader = new ServerSerializationStreamReader(
classLoader, serializationPolicyProvider);
streamReader.prepareToRead(encodedRequest);
// Read the name of the RemoteService interface
String serviceIntfName = streamReader.readString();
/*
* todo?? if (type != null) { if (!implementsInterface(type,
* serviceIntfName)) { // The service does not implement the requested
* interface throw new IncompatibleRemoteServiceException(
* "Blocked attempt to access interface '" + serviceIntfName +
* "', which is not implemented by '" + printTypeName(type) +
* "'; this is either misconfiguration or a hack attempt"); } }
*/
SerializationPolicy serializationPolicy = streamReader
.getSerializationPolicy();
Class<?> serviceIntf;
try
{
serviceIntf = RPC_getClassFromSerializedName(serviceIntfName,
classLoader);
if (!RemoteService.class.isAssignableFrom(serviceIntf))
{
// The requested interface is not a RemoteService interface
throw new IncompatibleRemoteServiceException(
"Blocked attempt to access interface '"
+ printTypeName(serviceIntf)
+ "', which doesn't extend RemoteService; this is either misconfiguration or a hack attempt");
}
} catch (ClassNotFoundException e)
{
throw new IncompatibleRemoteServiceException(
"Could not locate requested interface '" + serviceIntfName
+ "' in default classloader", e);
}
String serviceMethodName = streamReader.readString();
int paramCount = streamReader.readInt();
Class<?>[] parameterTypes = new Class[paramCount];
for (int i = 0; i < parameterTypes.length; i++)
{
String paramClassName = streamReader.readString();
try
{
parameterTypes[i] = RPC_getClassFromSerializedName(
paramClassName, classLoader);
} catch (ClassNotFoundException e)
{
throw new IncompatibleRemoteServiceException("Parameter " + i
+ " of is of an unknown type '" + paramClassName + "'", e);
}
}
try
{
Method method = serviceIntf.getMethod(serviceMethodName,
parameterTypes);
Object[] parameterValues = new Object[parameterTypes.length];
for (int i = 0; i < parameterValues.length; i++)
{
parameterValues[i] = streamReader
.deserializeValue(parameterTypes[i]);
}
return new SeamRPCRequest(method, parameterValues, parameterTypes,
serializationPolicy);
} catch (NoSuchMethodException e)
{
throw new IncompatibleRemoteServiceException(
formatMethodNotFoundErrorMessage(serviceIntf,
serviceMethodName, parameterTypes));
}
} catch (SerializationException ex)
{
throw new IncompatibleRemoteServiceException(ex.getMessage(), ex);
}
}
/**
* Returns the {@link Class} instance for the named class or primitive type.
*
* @param serializedName
* the serialized name of a class or primitive type
* @param classLoader
* the classLoader used to load {@link Class}es
* @return Class instance for the given type name
* @throws ClassNotFoundException
* if the named type was not found
*/
private static Class<?> RPC_getClassFromSerializedName(
String serializedName, ClassLoader classLoader)
throws ClassNotFoundException
{
Class<?> value = TYPE_NAMES.get(serializedName);
if (value != null)
{
return value;
}
return Class.forName(serializedName, false, classLoader);
}
/**
* Returns a string that encodes the result of calling a service method,
* which could be the value returned by the method or an exception thrown by
* it.
*
* <p>
* If the serializationPolicy parameter is not <code>null</code>, it is used
* to determine what types can be encoded as part of this response. If this
* parameter is <code>null</code>, then only subtypes of
* {@link com.google.gwt.user.client.rpc.IsSerializable IsSerializable} or
* types which have custom field serializers may be encoded.
* </p>
*
* <p>
* This method does no security checking; security checking must be done on
* the method prior to this invocation.
* </p>
*
* @param target
* instance on which to invoke the serviceMethod
* @param serviceMethod
* the method to invoke
* @param args
* arguments used for the method invocation
* @param serializationPolicy
* determines the serialization policy to be used
* @return a string which encodes either the method's return or a checked
* exception thrown by the method
*
* @throws NullPointerException
* if the serviceMethod or the serializationPolicy are
* <code>null</code>
* @throws SecurityException
* if the method cannot be accessed or if the number or type of
* actual and formal arguments differ
* @throws SerializationException
* if an object could not be serialized by the stream
* @throws UnexpectedException
* if the serviceMethod throws a checked exception that is not
* declared in its signature
*/
public static String RPC_invokeAndEncodeResponse(Object target,
Method serviceMethod, Class[] paramTypes, Object[] args,
SerializationPolicy serializationPolicy) throws SerializationException
{
if (serviceMethod == null)
{
throw new NullPointerException("serviceMethod");
}
if (serializationPolicy == null)
{
throw new NullPointerException("serializationPolicy");
}
String responsePayload;
try
{
GWTToSeamAdapter adapter = GWTToSeamAdapter.instance();
String serviceIntfName = serviceMethod.getDeclaringClass().getName();
GWTToSeamAdapter.ReturnedObject returnedObject = adapter
.callWebRemoteMethod(serviceIntfName, serviceMethod.getName(),
paramTypes, args);
// Object result = serviceMethod.invoke(target, args);
responsePayload = RPC.encodeResponseForSuccess(serviceMethod,
returnedObject.returnedObject, serializationPolicy);
} catch (IllegalAccessException e)
{
SecurityException securityException = new SecurityException(
formatIllegalAccessErrorMessage(target, serviceMethod));
securityException.initCause(e);
throw securityException;
} catch (IllegalArgumentException e)
{
SecurityException securityException = new SecurityException(
formatIllegalArgumentErrorMessage(target, serviceMethod, args));
securityException.initCause(e);
throw securityException;
} catch (InvocationTargetException e)
{
// Try to encode the caught exception
//
Throwable cause = e.getCause();
responsePayload = RPC.encodeResponseForFailure(serviceMethod, cause,
serializationPolicy);
}
return responsePayload;
}
/**
* Override this method to examine the serialized response that will be
* returned to the client. The default implementation does nothing and need
* not be called by subclasses.
*/
protected void RemoteServiceServlet_onAfterResponseSerialized(
String serializedResponse)
{
}
/**
* Override this method to examine the serialized version of the request
* payload before it is deserialized into objects. The default implementation
* does nothing and need not be called by subclasses.
*/
protected void RemoteServiceServlet_onBeforeRequestDeserialized(
String serializedRequest)
{
}
/**
* Override this method in order to control the parsing of the incoming
* request. For example, you may want to bypass the check of the Content-Type
* and character encoding headers in the request, as some proxies re-write
* the request headers. Note that bypassing these checks may expose the
* servlet to some cross-site vulnerabilities.
*
* @param request
* the incoming request
* @return the content of the incoming request encoded as a string.
*/
protected String RemoteServiceServlet_readContent(HttpServletRequest request)
throws ServletException, IOException
{
return RPCServletUtils.readContentAsUtf8(request, true);
}
public final SerializationPolicy getSerializationPolicy(
String moduleBaseURL, String strongName)
{
SerializationPolicy serializationPolicy = getCachedSerializationPolicy(
moduleBaseURL, strongName);
if (serializationPolicy != null)
{
return serializationPolicy;
}
serializationPolicy = doGetSerializationPolicy(getThreadLocalRequest(),
moduleBaseURL, strongName);
if (serializationPolicy == null)
{
// Failed to get the requested serialization policy; use the default
getServletContext()
.log(
"WARNING: Failed to get the SerializationPolicy '"
+ strongName
+ "' for module '"
+ moduleBaseURL
+ "'; a legacy, 1.3.3 compatible, serialization policy will be used. You may experience SerializationExceptions as a result.");
serializationPolicy = RPC.getDefaultSerializationPolicy();
}
// This could cache null or an actual instance. Either way we will not
// attempt to lookup the policy again.
putCachedSerializationPolicy(moduleBaseURL, strongName,
serializationPolicy);
return serializationPolicy;
}
private SerializationPolicy getCachedSerializationPolicy(
String moduleBaseURL, String strongName)
{
synchronized (serializationPolicyCache)
{
return serializationPolicyCache.get(moduleBaseURL + strongName);
}
}
private void putCachedSerializationPolicy(String moduleBaseURL,
String strongName, SerializationPolicy serializationPolicy)
{
synchronized (serializationPolicyCache)
{
serializationPolicyCache.put(moduleBaseURL + strongName,
serializationPolicy);
}
}
/**
* Gets the {@link SerializationPolicy} for given module base URL and strong
* name if there is one.
*
* Override this method to provide a {@link SerializationPolicy} using an
* alternative approach.
*
* @param request
* the HTTP request being serviced
* @param moduleBaseURL
* as specified in the incoming payload
* @param strongName
* a strong name that uniquely identifies a serialization policy
* file
* @return a {@link SerializationPolicy} for the given module base URL and
* strong name, or <code>null</code> if there is none
*/
protected SerializationPolicy doGetSerializationPolicy(
HttpServletRequest request, String moduleBaseURL, String strongName)
{
// The request can tell you the path of the web app relative to the
// container root.
String contextPath = request.getContextPath();
String modulePath = null;
if (moduleBaseURL != null)
{
try
{
modulePath = new URL(moduleBaseURL).getPath();
} catch (MalformedURLException ex)
{
// log the information, we will default
getServletContext().log(
"Malformed moduleBaseURL: " + moduleBaseURL, ex);
}
}
SerializationPolicy serializationPolicy = null;
/*
* Check that the module path must be in the same web app as the servlet
* itself. If you need to implement a scheme different than this, override
* this method.
*/
if (modulePath == null || !modulePath.startsWith(contextPath))
{
String message = "ERROR: The module path requested, "
+ modulePath
+ ", is not in the same web application as this servlet, "
+ contextPath
+ ". Your module may not be properly configured or your client and server code maybe out of date.";
getServletContext().log(message);
} else
{
// Strip off the context path from the module base URL. It should be a
// strict prefix.
String contextRelativePath = modulePath
.substring(contextPath.length());
String serializationPolicyFilePath = SerializationPolicyLoader
.getSerializationPolicyFileName(contextRelativePath + strongName);
// Open the RPC resource file read its contents.
InputStream is = getServletContext().getResourceAsStream(
serializationPolicyFilePath);
try
{
if (is != null)
{
try
{
serializationPolicy = SerializationPolicyLoader
.loadFromStream(is, null);
} catch (ParseException e)
{
getServletContext().log(
"ERROR: Failed to parse the policy file '"
+ serializationPolicyFilePath + "'", e);
} catch (IOException e)
{
getServletContext().log(
"ERROR: Could not read the policy file '"
+ serializationPolicyFilePath + "'", e);
}
} else
{
String message = "ERROR: The serialization policy file '"
+ serializationPolicyFilePath
+ "' was not found; did you forget to include it in this deployment?";
getServletContext().log(message);
}
} finally
{
if (is != null)
{
try
{
is.close();
} catch (IOException e)
{
// Ignore this error
}
}
}
}
return serializationPolicy;
}
private void RemoteServiceServlet_writeResponse(HttpServletRequest request,
HttpServletResponse response, String responsePayload)
throws IOException
{
boolean gzipEncode = RPCServletUtils.acceptsGzipEncoding(request)
&& shouldCompressResponse(request, response, responsePayload);
RPCServletUtils.writeResponse(getServletContext(), response,
responsePayload, gzipEncode);
}
/**
* Override this method to control what should happen when an exception
* escapes the {@link #processCall(String)} method. The default
* implementation will log the failure and send a generic failure response to
* the client.
* <p/>
*
* An "expected failure" is an exception thrown by a service method that is
* declared in the signature of the service method. These exceptions are
* serialized back to the client, and are not passed to this method. This
* method is called only for exceptions or errors that are not part of the
* service method's signature, or that result from SecurityExceptions,
* SerializationExceptions, or other failures within the RPC framework.
* <p/>
*
* Note that if the desired behavior is to both send the GENERIC_FAILURE_MSG
* response AND to rethrow the exception, then this method should first send
* the GENERIC_FAILURE_MSG response itself (using getThreadLocalResponse),
* and then rethrow the exception. Rethrowing the exception will cause it to
* escape into the servlet container.
*
* @param e
* the exception which was thrown
*/
protected void RemoteServiceServlet_doUnexpectedFailure(Throwable e)
{
ServletContext servletContext = getServletContext();
RPCServletUtils.writeResponseForUnexpectedFailure(servletContext,
getThreadLocalResponse(), e);
}
/**
* Determines whether the response to a given servlet request should or
* should not be GZIP compressed. This method is only called in cases where
* the requester accepts GZIP encoding.
* <p>
* This implementation currently returns <code>true</code> if the response
* string's estimated byte length is longer than 256 bytes. Subclasses can
* override this logic.
* </p>
*
* @param request
* the request being served
* @param response
* the response that will be written into
* @param responsePayload
* the payload that is about to be sent to the client
* @return <code>true</code> if responsePayload should be GZIP compressed,
* otherwise <code>false</code>.
*/
protected boolean shouldCompressResponse(HttpServletRequest request,
HttpServletResponse response, String responsePayload)
{
return RPCServletUtils
.exceedsUncompressedContentLengthLimit(responsePayload);
}
private static String formatMethodNotFoundErrorMessage(Class<?> serviceIntf,
String serviceMethodName, Class<?>[] parameterTypes)
{
StringBuffer sb = new StringBuffer();
sb.append("Could not locate requested method '");
sb.append(serviceMethodName);
sb.append("(");
for (int i = 0; i < parameterTypes.length; ++i)
{
if (i > 0)
{
sb.append(", ");
}
sb.append(printTypeName(parameterTypes[i]));
}
sb.append(")'");
sb.append(" in interface '");
sb.append(printTypeName(serviceIntf));
sb.append("'");
return sb.toString();
}
private static String formatIllegalAccessErrorMessage(Object target,
Method serviceMethod)
{
StringBuffer sb = new StringBuffer();
sb.append("Blocked attempt to access inaccessible method '");
sb.append(getSourceRepresentation(serviceMethod));
sb.append("'");
if (target != null)
{
sb.append(" on target '");
sb.append(printTypeName(target.getClass()));
sb.append("'");
}
sb.append("; this is either misconfiguration or a hack attempt");
return sb.toString();
}
private static String formatIllegalArgumentErrorMessage(Object target,
Method serviceMethod, Object[] args)
{
StringBuffer sb = new StringBuffer();
sb.append("Blocked attempt to invoke method '");
sb.append(getSourceRepresentation(serviceMethod));
sb.append("'");
if (target != null)
{
sb.append(" on target '");
sb.append(printTypeName(target.getClass()));
sb.append("'");
}
sb.append(" with invalid arguments");
if (args != null && args.length > 0)
{
sb.append(Arrays.asList(args));
}
return sb.toString();
}
/**
* Returns the source representation for a method signature.
*
* @param method
* method to get the source signature for
* @return source representation for a method signature
*/
private static String getSourceRepresentation(Method method)
{
return method.toString().replace('$', '.');
}
/**
* Straight copy from
* {@link com.google.gwt.dev.util.TypeInfo#getSourceRepresentation(Class)} to
* avoid runtime dependency on gwt-dev.
*/
private static String printTypeName(Class<?> type)
{
// Primitives
//
if (type.equals(Integer.TYPE))
{
return "int";
} else if (type.equals(Long.TYPE))
{
return "long";
} else if (type.equals(Short.TYPE))
{
return "short";
} else if (type.equals(Byte.TYPE))
{
return "byte";
} else if (type.equals(Character.TYPE))
{
return "char";
} else if (type.equals(Boolean.TYPE))
{
return "boolean";
} else if (type.equals(Float.TYPE))
{
return "float";
} else if (type.equals(Double.TYPE))
{
return "double";
}
// Arrays
//
if (type.isArray())
{
Class<?> componentType = type.getComponentType();
return printTypeName(componentType) + "[]";
}
// Everything else
//
return type.getName().replace('$', '.');
}
}