/*
*
*/
package org.smartly.commons.remoting.rpc;
import org.smartly.commons.logging.Level;
import org.smartly.commons.logging.Logger;
import org.smartly.commons.logging.util.LoggingUtils;
import org.smartly.commons.util.BeanUtils;
import org.smartly.commons.util.ConversionUtils;
import org.smartly.commons.util.FormatUtils;
import org.smartly.commons.remoting.rpc.descriptor.MethodDescriptor;
import org.smartly.commons.remoting.rpc.descriptor.ServiceDescriptor;
import java.lang.reflect.Method;
import java.util.*;
/**
* @author angelo.geminiani
*/
public class RemoteInvoker {
public static int POOL_SIZE = 3; // 3 services per instance
private final Map<Class, List<Object>> _servicePools;
public RemoteInvoker() {
_servicePools = Collections.synchronizedMap(new HashMap<Class, List<Object>>());
}
public Object call(final String endpoint,
final String serviceName, final String methodName,
final Map<String, String> parameters) throws Exception {
return this.call(null, endpoint, serviceName, methodName, parameters);
}
public Object call(final IRemoteContext context, final String endpoint,
final String serviceName, final String methodName,
final Map<String, String> parameters) throws Exception {
final ServiceDescriptor service = RemoteServiceRepository.getInstance().getService(
endpoint, serviceName);
if (null != service) {
return this.call(context, service, methodName, parameters);
} else {
throw new Exception(FormatUtils.format("Service not found: {0}", serviceName));
}
}
// ------------------------------------------------------------------------
// p r i v a t e
// ------------------------------------------------------------------------
private Logger getLogger() {
return LoggingUtils.getLogger(this);
}
private Object popServiceFromPool(final Class aclass) {
synchronized (_servicePools) {
if (_servicePools.containsKey(aclass)) {
// already have pool for service
final List<Object> pool = _servicePools.get(
aclass);
if (pool.size() > 0) {
// pop service from pool
return pool.remove(0);
}
} else {
// creates new service pool
final List<Object> pool = new ArrayList<Object>();
_servicePools.put(aclass, pool);
}
// creates new service
return this.createService(aclass);
}
}
private void putServiceInPool(final Object serviceInstance) {
synchronized (_servicePools) {
final List<Object> pool = _servicePools.get(
serviceInstance.getClass());
if (null != pool
&& pool.size() < POOL_SIZE) {
pool.add(serviceInstance);
} else {
this.getLogger().log(Level.INFO,
FormatUtils.format("SERVICE POOL SIZE ({0}) NOT ENOUGHT."
+ " New service instance was created out of pool to "
+ " serve requests.",
POOL_SIZE));
}
}
}
private Object createService(final Class aclass) {
try {
final Object result = aclass.newInstance();
return result;
} catch (Throwable t) {
this.getLogger().log(Level.SEVERE, null, t);
}
return null;
}
private Object call(final IRemoteContext context,
final ServiceDescriptor service, final String methodName,
final Map<String, String> stringParamValues) throws Exception {
// retrieve service from pool ( Thread SAFE )
final Object serviceInstance = this.popServiceFromPool(
service.getServiceClass());
try {
if (null != serviceInstance) {
final MethodDescriptor method = service.getMethod(methodName);
if (null != method) {
final Map<String, Object> paramValues;
final Map<String, Class> paramTypes = method.getParameters();
if (paramTypes.size() > 0) {
paramValues = ConversionUtils.toTypes(stringParamValues,
paramTypes);
} else {
paramValues = null;
}
return this.call(context,
serviceInstance, methodName,
paramValues, paramTypes);
} else {
// method not found
this.getLogger().warning(FormatUtils.format(
"Method '{0}' not found!", methodName));
}
}
} finally {
// reintroduce service in pool
this.putServiceInPool(serviceInstance);
}
return null;
}
private Object call(final IRemoteContext context,
final Object serviceInstance, final String methodName,
final Map<String, Object> paramValues,
final Map<String, Class> paramTypes) throws Exception {
final Object[] values = null != paramValues
? paramValues.values().toArray(new Object[paramValues.size()])
: new Object[0];
final Class[] types = null != paramTypes
? paramTypes.values().toArray(new Class[paramTypes.size()])
: new Class[0];
return this.call(context, serviceInstance, methodName, values, types);
}
private Object call(final IRemoteContext context,
final Object serviceInstance,
final String methodName, final Object[] parameters,
final Class[] paramTypes) throws Exception {
//-- add context if enabled --//
if (null!=context && serviceInstance instanceof RemoteServiceContext) {
((RemoteServiceContext) serviceInstance).setContext(context);
}
//-- execute method --//
final Method method = BeanUtils.getMethodIfAny(serviceInstance.getClass(),
methodName, paramTypes);
if (null != method) {
return method.invoke(serviceInstance, parameters);
} else {
//-- method not found in service --//
final String msg = FormatUtils.format(
"Method '{0}' not found in service '{1}'.",
methodName,
serviceInstance.getClass().getName());
throw new Exception(FormatUtils.format("Service not found: {0}", msg));
}
}
// ------------------------------------------------------------------------
// S T A T I C
// ------------------------------------------------------------------------
private final static RemoteInvoker __INSTANCE = new RemoteInvoker();
public static RemoteInvoker getInstance() {
return __INSTANCE;
}
}