/*********************************************************************
* DefaultRemotingHandler.java
* created on 09.07.2006 by netseeker
* $$Source$$
* $$Date$$
* $$Revision$$
*
* ====================================================================
*
* Copyright 2006 netseeker aka Michael Manske
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*
* This file is part of the EJOE framework.
* For more information on the author, please see
* <http://www.manskes.de/>.
*
*********************************************************************/
package de.netseeker.ejoe.handler;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import de.netseeker.ejoe.cache.LRUMap;
import de.netseeker.ejoe.util.ContentStringBuilder;
/**
* Reflection based ServerHandler. This is the default implementation for EJOE's remote reflection feature. This class
* handles automatic mapping of object types to primitives for the method parameter to find the right method signature.
* Classes, parameter classes (including object-primitive-mapping) as well as methods will be cached to outperform usual
* reflection.
*
* @author netseeker
* @since 0.3.9.1
*/
public class DefaultRemotingHandler extends BaseRemotingHandler
{
/**
*
*/
private static final long serialVersionUID = 1L;
private static final Logger log = Logger.getLogger( DefaultRemotingHandler.class.getName() );
private Map constructorCache = new LRUMap();
private Map methodCache = new LRUMap();
private Map paramCache = new LRUMap();
Object handle( Class target, String method, Object[] args ) throws Exception
{
Object ret = null;
Object[] types = getArgTypes( args );
Class[] argTypes = (Class[]) types[0];
Class[] argTypesPrimitive = (Class[]) types[1];
if ( log.isLoggable( Level.FINE ) )
{
log.log( Level.FINE, target.getName() + '#' + method + '(' + ContentStringBuilder.toString( argTypes )
+ ')' );
}
/* Java 5 break compatibility with Java <= 1.4, because SUN changed the behavior of Class.getName()
* so the following code would break Java 1.4 backward compatibility
if ( !target.getSimpleName().equals( method ) )
{
Method mtd = getMethodForArgumentTypes( target, method, argTypes, argTypesPrimitive );
Object o = target.newInstance();
ret = mtd.invoke( o, args );
}
else
{
Constructor constructor = getConstructorForArgumentTypes( target, argTypes, argTypesPrimitive );
ret = constructor.newInstance( args );
}
*/
try
{
Method mtd = getMethodForArgumentTypes( target, method, argTypes, argTypesPrimitive );
Object o = target.newInstance();
ret = mtd.invoke( o, args );
}
catch(NoSuchMethodException e)
{
Constructor constructor = getConstructorForArgumentTypes( target, argTypes, argTypesPrimitive );
ret = constructor.newInstance( args );
}
return ret;
}
/**
* @param params array of objects for which the argument types has to be determined
* @return an array of two class-arrays (Class[]) containing object types in the first class-array as well as their
* primitive representation in the second class-array
*/
protected Object[] getArgTypes( Object[] params )
{
Class[] argTypes = null;
Class[] argTypesPrimitive = null;
if ( params != null && params.length > 0 )
{
// now we have to create an array of parameter types to find the
// right method in the required class via reflection
argTypes = new Class[params.length];
// because we might get primitive parameters as objects, we have to
// create a second array of parameter types
// containing the corresponding primitve types
argTypesPrimitive = new Class[params.length];
for ( int i = 0; i < params.length; i++ )
{
// add the object type to the regular array of parameter types
argTypes[i] = params[i].getClass();
// now check if the parameter is a object type instead of a
// primitive
if ( !argTypes[i].isPrimitive() )
{
// get the class name of the object type
String clName = argTypes[i].getName();
// do we have the corresponding primitive type already in
// cache?
if ( !paramCache.containsKey( clName ) )
{
try
{
// no, hence we have determine the corresponding
// primitive class via reflection
Field field = argTypes[i].getField( "TYPE" );
argTypesPrimitive[i] = (Class) field.get( params[i] );
// add the primitive type to the cache
paramCache.put( clName, argTypesPrimitive[i] );
}
catch ( NoSuchFieldException e )
{
// ok, can happen if the object type just has no
// corresponding primitive type
argTypesPrimitive[i] = argTypes[i];
}
catch ( Exception e )
{
// shouldn't happen
argTypesPrimitive[i] = argTypes[i];
}
}
// the corresponding primitive type was found in the cache
else
{
argTypesPrimitive[i] = (Class) paramCache.get( clName );
}
}
// the parameter was given as primitive
else
{
argTypesPrimitive[i] = argTypes[i];
}
}
}
// ok, no parameters given
else
{
argTypes = new Class[] {};
argTypesPrimitive = new Class[] {};
}
return new Object[] { argTypes, argTypesPrimitive };
}
/**
* Similiar to {@link #getMethodForArgumentTypes(Class, String, Class[], Class[])} but tries to lookup constructors
* instead
*
* @param clazz
* @param argTypes
* @param argTypesPrimitive
* @return
* @throws SecurityException
* @throws NoSuchMethodException
*/
protected Constructor getConstructorForArgumentTypes( Class clazz, Class[] argTypes, Class[] argTypesPrimitive )
throws SecurityException, NoSuchMethodException
{
String className = clazz.getName();
// try to find the method in the constructor-cache while using the array
// of
// object types for the given parameters
Constructor constr = (Constructor) constructorCache.get( className + ContentStringBuilder.toString( argTypes ) );
if ( constr == null )
{
// ok, then try to find the constructor in the constructor-cache
// while using
// the array of primitive types for the given parameters
constr = (Constructor) constructorCache
.get( className + ContentStringBuilder.toString( argTypesPrimitive ) );
}
if ( constr == null )
{
// seems as this is the first search for the constructor
try
{
// try to get the constructor via reflection while using the
// array of
// object types for the given parameters
constr = clazz.getConstructor( argTypes );
constructorCache.put( className + ContentStringBuilder.toString( argTypes ), constr );
}
catch ( NoSuchMethodException e )
{
// ok, maybe the parameter types don't match, lets try the array
// of primitive types for the given parameters
constr = clazz.getConstructor( argTypesPrimitive );
constructorCache.put( className + ContentStringBuilder.toString( argTypesPrimitive ), constr );
}
}
return constr;
}
/**
* @param clazz
* @param mName
* @param argTypes
* @param argTypesPrimitive
* @return
* @throws SecurityException
* @throws NoSuchMethodException
*/
protected Method getMethodForArgumentTypes( Class clazz, String mName, Class[] argTypes, Class[] argTypesPrimitive )
throws SecurityException, NoSuchMethodException
{
String key = clazz.getName() + '#' + mName;
String argTypesKey = ContentStringBuilder.toString( argTypes );
// try to find the method in the method-cache while using the array of
// object types for the given parameters
Method mtd = (Method) methodCache.get( key + argTypesKey );
if ( mtd == null && argTypesPrimitive != null )
{
// ok, then try to find the method in the method-cache while using
// the array of primitive types for the given parameters
mtd = (Method) methodCache.get( key + ContentStringBuilder.toString( argTypesPrimitive ) );
}
if ( mtd == null )
{
// seems as this is the first invocation of the method
try
{
// try to get the method via reflection while using the array of
// object types for the given parameters
mtd = clazz.getMethod( mName, argTypes );
methodCache.put( key + ContentStringBuilder.toString( argTypes ), mtd );
}
catch ( NoSuchMethodException e )
{
// ok, maybe the parameter types don't match, lets try the array
// of primitive types for the given parameters
if ( argTypesPrimitive != null )
{
mtd = clazz.getMethod( mName, argTypesPrimitive );
methodCache.put( key + ContentStringBuilder.toString( argTypesPrimitive ), mtd );
}
else
{
throw new NoSuchMethodException( key + argTypesKey );
}
}
}
return mtd;
}
}