General

How can I use the different calls of services?

Client - Server

This framework is for the client side. Every client need of the other side a server. You can test the client with a mini server (implementation of net.sf.crispy.impl.MiniServer interface). Before you can use a implementation from a client, you have to start a equivalent server implementation.

For example for the RMI-client you start the server net.sf.crispy.rmi.MiniRmiServer or your own implementation.

Synchronous/Asynchronous execution

The default execution of the service is synchronous:

	
Properties prop = new Properties();
prop.put(Property.STATIC_PROXY_CLASS, StaticBurlapProxy.class.getName());
prop.put(Property.REMOTE_URL_AND_PORT, "http://localhost:8093/crispy/burlap");
			
ServiceManager manager = new ServiceManager(prop);
Echo e = (Echo)  manager.createService(Echo.class);
...					

The condition for a asynchronous execution is a implementation of the interface net.sf.crispy.concurrent.AsynchronousCallback. The implementation of this interface must be registered by the ServiceManager. The methods handleResult or handleError are calling, if the result of method execution is available.

You can switch to asynchronous execution of two kinds. The first way is a global property for all service executions:

Properties prop = new Properties();
prop.put(Property.STATIC_PROXY_CLASS, StaticBurlapProxy.class.getName());
prop.put(Property.REMOTE_URL_AND_PORT, "http://localhost:8093/crispy/burlap");
// set a implementation of the interface: net.sf.crispy.concurrent.AsynchronousCallback
prop.put(Property.ASYNCHRONOUS_CALLBACK_CLASS, AsynchronousCallbackForTests.class.getName());
// maximum size of Threads			
prop.put(Property.ASYNCHRONOUS_MAX_SIZE_OF_THREADS, "3");
			
ServiceManager manager = new ServiceManager(prop);
Echo e = (Echo)  manager.createService(Echo.class);
...					

The second way is a local property for one service executions:

	
Properties prop = new Properties();
prop.put(Property.STATIC_PROXY_CLASS, StaticBurlapProxy.class.getName());
prop.put(Property.REMOTE_URL_AND_PORT, "http://localhost:8093/crispy/burlap");
			
ServiceManager manager = new ServiceManager(prop);
AsynchronousCallback callback = new AsynchronousCallbackForTests();
// the last two args are a method-filter and maximum size of Threads
// the method ping is synchronous, all other Echo-methods are asynchronous
Echo e = (Echo)  manager.createService(Echo.class, callback, new String [] {"ping"} , 8);
...					

=> Hint: By Static Proxy must be set Dynamic Proxy for intercept all method calls.

		
Properties prop = new Properties();
prop.put(Property.STATIC_PROXY_CLASS, StaticCorbaProxy.class.getName());
prop.put(Property.DYNAMIC_PROXY_CLASS, Property.VALUE_FOR_JDK_DYNAMIC_PROXY);

To switch from asynchronous to synchronous, you can remove the AsynchronousCallback by the ServiceManager. A switch back to asynchronous execution is not possible.

		
Properties prop = new Properties();
...
ServiceManager manager = new ServiceManager(prop);
...
// after the remove of the AsynchronousCallback are all calls from the Echo class synchronous.
manager.removeAsynchronousCallback(Echo.class);

The call order

For a better invocation control, you can add interceptor or/and modifier implementation (see section: Properties/Configuration). The call order of register interceptor and modifier implementation is:

  1. call net.sf.crispy.Modifier - modifyBeforeInvocation(InterceptorContext)
  2. call net.sf.crispy.Interceptor - beforeMethodInvocation(InterceptorContext)
  3. execute remote invocation
  4. if a Exception is thrown, then call - net.sf.crispy.Interceptor onError(Throwable)
  5. call net.sf.crispy.Modifier - modifyAfterInvocation(InterceptorContext)
  6. call net.sf.crispy.Interceptor - afterMethodInvocation(InterceptorContext)

The Modifier

Example:

			
public class MyBeforeModifier implements Modifier {

  public InterceptorContext modifyBeforeInvocation( InterceptorContext interceptorContext) {
     String methodName = interceptorContext.getMethod().getName();	
     if (methodName.equals("add")) {
        interceptorContext.setArgs(new Integer[]{new Integer(1), new Integer(2)});
     }
     return interceptorContext;
  }

  public InterceptorContext modifyAfterInvocation(InterceptorContext interceptorContext) {		
     return interceptorContext;
  }
}

The Interceptor

Example:

			
public class LogInterceptor implements Interceptor {

  protected static Log log = LogFactory.getLog (LogInterceptor.class);

  public void beforeNewInstance(Class interfaceClass) {
     log.info("beforeNewInstance: " + interfaceClass);
  }

  public void afterNewInstance(Class interfaceClass, Object proxyObject) {
     log.info("afterNewInstance: " + interfaceClass + " - " + proxyObject);
  }

  public void beforeMethodInvocation(InterceptorContext interceptorContext) {
     log.info("beforeMethodInvokation: " + interceptorContext);
  }

  public void afterMethodInvocation(InterceptorContext interceptorContext) {
     log.info("afterMethodInvokation: " + interceptorContext);
  }

  public void onError(Throwable throwable) {
     log.info("onError: " + throwable);
  }

Register Modifier/Interceptor

Example:

			
  Properties properties = new Properties();
  properties.put(Property.INTERCEPTOR_CLASS, LogInterceptor.class.getName());
  properties.put(Property.MODIFIER_CLASS, MyBeforeModifier.class.getName());
  IServiceManager serviceManager = new ServiceManager(properties);

  // OR		
  IServiceManager serviceManager = new ServiceManager(...);
  serviceManager.addInterceptor(new LogInterceptor ());
  serviceManager.setModifier(new MyBeforeModifier ());

Example for interrupt method invocation with a Interceptor

The method invocation is interruptable, with the flag InterceptorContext.setInterruptIncocation(true). This is helpful by a client security or by a client cache, where the remote methode don't execute. With the method InterceptorContext.setResult(value) can set the return value, without execute method. The return value can be a value or a Exception.

Example:

			
public class InterruptInterceptor implements Interceptor {

  ...
  
  public void beforeMethodInvocation(InterceptorContext interceptorContext) {
     Object result = MyCache.get(interceptorContext.getMethod(), interceptorContext.getArgs());
     if (result != null) {
     	interceptorContext.setInterruptInvocation(true);
     	interceptorContext.setResult(result);
     }
  }
  
  // Or a Exception

  public void beforeMethodInvocation(InterceptorContext interceptorContext) {
     String methodName = interceptorContext.getMethod().getName();
     if (MySecurityManager.checkPermission(methodName, getCurrentUser())) {
     	interceptorContext.setInterruptInvocation(true);
     	interceptorContext.setResult(new MySecurityException("No Permission to execute the method: " + methodName);
     }
  }
  
}

Filter for Interceptor

For a better control of Interceptors you can add a InterceptorFilter. With the Filter you can enable or disable the call from Interceptors. It is a possibility to control the Interceptors for a better performance or outline.

Example:

			

  ...
  
  ServiceManager serviceManager = new ServiceManager(properties);
  serviceManager.addInterceptor(new WaitInterceptor());
  serviceManager.addInterceptor(new StopWatchInterceptor());
  
  SimpleNameInterceptorFilter filter = new SimpleNameInterceptorFilter(new String[] {"ping", "add"}, 
  									 SimpleNameInterceptorFilter.FILTER_TYPE_METHOD);
  serviceManager.addInterceptorFilter(filter);
  
  Echo echo = (Echo) serviceManager.createService(Echo.class);
  // the Interceptor is filtered, the Interceptor is executed (stop time is greater 0)
  echo.ping();
  
  // the Interceptor is NOT executed
  echo.echo("Hello Crispy!");

Static vs Dynamic Proxy

See details in Developers Guide in section Proxy Pattern.

Static Proxy - Property.STATIC_PROXY_CLASS

The convention for the Static Proxy name class is: Static[transport provider]Proxy.

Transport Static Proxy
Burlap and Hessian (Caucho) StaticBurlapProxy / StaticHessianProxy
EJB StaticEjbProxy
Static RMI StaticRmiProxy
CORBA StaticCorbaProxy
Local object calls StaticLocalObjectProxy

Dynamic Proxy - Property.EXECUTOR_CLASS

The convention for the Dynamic Proxy name class is: [transport provider]Executer.

Transport Dynamic Proxy / Executer
XML-RPC XmlRpcExecutor
JAX-RPC JaxRpcExecutor
REST RestExecutor
JBoss Remoting JBossRemotingExecutor
Dynamic RMI RmiExecutor

Server name/port and invocation strategy

All transport provider required a url to invoke the remote invokation. In this url is coded the server name and server port and the service and the service method. The server name and port is describe in the properties:

Properties prop = new Properties();
prop.put(Property.REMOTE_URL_AND_PORT, "http://www.myserver.de:8080");
prop.put(Property.INVOCATION_STRATEGY, ClassPlusMethodInvocationStrategy.class.getName());

The service name and method is coded with the net.sf.crispy.InvocationStrategy, for Example:

		
public class ClassPlusMethodInvocationStrategy implements InvocationStrategy {

  public Object convert(Map propertyMap) {
     String clazz = (String) propertyMap.get(InvocationStrategy.CLASS_NAME);
     String method = (String) propertyMap.get(InvocationStrategy.METHOD_NAME);
     return clazz + "." + method;
  }

}

Transport URL plus Invocation Strategy Example
Burlap and Hessian (Caucho) UrlAndPort + "/" + InterfaceName http://www.myserver.de:8080/crispy/hessian/test.crispy.example.service.Echo
EJB with properties -
Static RMI UrlAndPort + "/" + LookupName http://rmi://myserver:1099/RemoteEcho
Local object calls local Java Object calls -
XML-RPC NameSpacePlusMethodInvocationStrategy http://myserver:9090/test.crispy.example.service.Calculator.add
JAX-RPC UrlPlusClassNameInvocationStrategy http://myserver:80/axis/services/Calculator
REST RestInvocationStrategy http://myserver:8095/crispy/rest?class=test.crispy.example.service.Calculator&method=add
JBoss Remoting ClassMethodMapInvocationStrategy socket://myserver:5411
Dynamic RMI - rmi://myserver:1099/RmiInvokationHandler

Properties/Configuration to describe the communication

General Properties, independent of the choice of the client technology. All properties are optional and can combinate jointly.

All properties declaretions are Strings!

  • DYNAMIC_PROXY_CLASS - Property.VALUE_FOR_JDK_DYNAMIC_PROXY (is default) or Property.VALUE_FOR_CGLIB_DYNAMIC_PROXY or your own implementation of net.sf.crispy.DynamicProxy
  • INTERCEPTOR_CLASS, INTERCEPTOR_CLASS_2, INTERCEPTOR_CLASS_3 - You can add interceptor (net.sf.crispy.Interceptor) classes. For example net.sf.crispy.interceptor.LogInterceptor for print logs.
  • MODIFIER_CLASS - You can add modifier class with implementation from the net.sf.crispy.Modifier interface. In this class, you can modify the parameter befor the method is executed or you can convert the result after the execution. With the class net.sf.crispy.impl.ModifierChain you can link together several Modifier in a chain. The output from the first Modifier is the input for the next (second) Modifier in the chain and so on.
  • INVOCATION_STRATEGY and INVOCATION_STRATEGY_NAMESPACE - A remote call has the url and port from the server. The url has different construction. A XML-RPC url may be: http://localhost:8080/sf.net.service.Echo.echo -> [url] + [class] + [method] or http://localhost:8080/services.echo -> [url] + [namespace] + [method] (INVOCATION_STRATEGY_NAMESPACE correspond with [namespace]).
  • WITH_CONVERTER - Convert parameter before invoke method (net.sf.crispy.utilConverter.makeSimple()) and result after invoke methode (net.sf.crispy.utilConverter.makeComplex()).
  • SECURITY_PASSWD and SECURITY_USER - Are properties for the future to manage security. For example, to get a identify Token for the next calls.
  • DEBUG_MODE_ON - By search error or analyse the system in detail. This option can extend with set the logger to debug mode.
  • NULL_VALUE - If object properties can be null and the transport not supported null values (XML-RPC for example) you can substitute the null value with this property.
  • ASYNCHRONOUS_CALLBACK_CLASS - A class name of a implementation of the interface: net.sf.crispy.concurrent.AsynchronousCallback. With set this properties, all executions are asynchronous. (ASYNCHRONOUS_MAX_SIZE_OF_THREADS, with this extension you can influence the maximum size of Threads.)

Properties define in the source code

Property example:

Properties prop = new Properties();
prop.put(Property.DYNAMIC_PROXY_CLASS, Property.VALUE_FOR_CGLIB_DYNAMIC_PROXY); 

prop.put(Property.INTERCEPTOR_CLASS, LogInterceptor.class.getName());
prop.put(Property.INTERCEPTOR_CLASS_2, StopWatchInterceptor.class.getName());
        
prop.put(Property.SECURITY_USER, "user");
prop.put(Property.SECURITY_PASSWD, "passwd");

Load property from file

Properties can create of two kinds. It is possible to do the properties in a file or the second possibillity is to create the properties in the programming sources.

Are the properties in a file, than can load this properties of different kinds:

  • net.sf.crispy.properties.ClassPathPropertiesLoader: the porperty file can find in the java class path.
    	
    PropertiesLoader loader = new ClassPathPropertiesLoader("example.properties");
    
  • net.sf.crispy.properties.ClassPropertiesLoader: the property file is in the same package, how the class.
    	
    PropertiesLoader loader = new ClassPropertiesLoader(this.getClass(), "example.properties");
    
  • net.sf.crispy.properties.FilePropertiesLoader: the file can load how every file with the file and directory path.
    	
    PropertiesLoader loader = new FilePropertiesLoader("c:/temp/example.properties");
    
  • net.sf.crispy.properties.UrlPropertiesLoader: file can load with a url to the file.
    	
    PropertiesLoader loader = new UrlPropertiesLoader("file://c:/temp/example.properties");
    

If properties are loaded, then can the ServiceManager get the properties and work:

	
PropertiesLoader loader = new ClassPathPropertiesLoader("example.properties");
ServiceManager manager = new ServiceManager(loader);
Calculator calculator = (Calculator) serviceManager.createService(Calculator.class);
int result = calculator.add(2, 3);

A Example for a property file:

		
crispy.prop.server.url=http://localhost:9090
crispy.prop.executor.class=net.sf.crispy.impl.XmlRpcExecutor

Different Clients

In this section i will describe the special properties for the client invocation.

Http call with Java Serializer

Properties:

  • EXECUTOR_CLASS - The executor class. net.sf.crispy.impl.HttpExecutor
  • REMOTE_URL_AND_PORT (optional) - The server URL and port. Default value is http://localhost:8111/crispy/httpserializer

Example:

Properties prop = new Properties();
prop.put(Property.EXECUTOR_CLASS, HttpExecutor.class.getName());
prop.put(Property.REMOTE_URL_AND_PORT, "http://www.my.de:8111/crispy/httpserializer");

Hint: All transfer object must implement the interface java.io.Serializable. You can deal with this restriction by set the Property.WITH_CONVERTER of true. This option is used be non great transfer objects.

XML-RPC

Properties:

  • EXECUTOR_CLASS - The executor class. net.sf.crispy.impl.XmlRpcExecutor
  • REMOTE_URL_AND_PORT (optional) - The server URL and port. Default value is http://localhost:8080

Example:

Properties prop = new Properties();
prop.put(Property.EXECUTOR_CLASS, XmlRpcExecutor.class.getName());
prop.put(Property.REMOTE_URL_AND_PORT, "http://www.my.de:8081");

Server: If you want to use complex parameter objects (for example a customer with addresses), you have to add the handler net.sf.crispy.xmlrpc.XmlRpcConvertHandler. This handler make the marshalling and unmarshalling (how the MiniXmlRpcServer).

RMI

Crispy know two kinds of RMI invocation:

  • Static: Is the traditional method. The service interface must implement the java.rmi.Remote interface and the method must thrown java.rmi.RemoteException. The implementation extends the java.rmi.server.UnicastRemoteObject and is bind on the RMI server (Naming.rebind([LookUp-Name]).
  • Dynamic: Is a Java Interface without restriction. The call is wrapped in the net.sf.crispy.impl.rmi.RmiInvokationHandler.

Static RMI:

Properties:

  • STATIC_PROXY_CLASS - The proxy creater class. net.sf.crispy.impl.StaticRmiProxy
  • Service-Name and LookUp-Name The service is mapped to lookup-name.
  • REMOTE_URL_AND_PORT (optional) - The server URL and port. Default value is rmi://localhost:1099

Example:

Properties prop = new Properties();
prop.put(Property.STATIC_PROXY_CLASS, StaticRmiProxy.class.getName());
prop.put(Property.REMOTE_URL_AND_PORT, "http://www.my.de:1099");
// LookUp - name for Naming.rebind()
prop.put(RemoteCalculator.class.getName(), RemoteCalculator.LOOKUP_NAME);

Dynamic RMI:

Properties:

  • EXECUTOR_CLASS - The executor class. net.sf.crispy.impl.RmiExecutor
  • REMOTE_URL_AND_PORT (optional) - The server URL and port. Default value is rmi://localhost:1099

Example:

Properties prop = new Properties();
prop.put(Property.EXECUTOR_CLASS, RmiExecutor.class.getName());
prop.put(Property.REMOTE_URL_AND_PORT, "http://www.my.de:1099");

Server: If you want to use complex parameter objects, you have to add the handler net.sf.crispy.rmi.RmiInvokationHandlerImpl. This handler make the marshalling and unmarshalling (how the MiniRmiServer).

EJB

Properties:

  • JNDI_NAME - The interface is mapped to the JNDI name.
  • EJB_HOME_INTERFACE - The home interface, create the instance.
  • STATIC_PROXY_CLASS - The executor class. net.sf.crispy.impl.StaticEjbProxy

Example (EjbService extends EJBObject):

Properties prop = new Properties();
prop.put(EjbService.class.getName(), EjbService.JNDI_NAME);
prop.put(EjbService.class.getName() + Property.EJB_HOME_INTERFACE, EjbServiceHome.class.getName());
prop.put(Property.STATIC_PROXY_CLASS, StaticEjbProxy.class.getName());

Server: Crispy don't have a server (container) implementation. You can test your EJB service with MockEJB or with a J2EE container implementation.

JAX-RPC

Properties:

  • TypeMappingFactory.PROPERTY_TYPE_MAPPING_FACTORY, PROPERTY_TYPE_MAPPING_FACTORY_2, PROPERTY_TYPE_MAPPING_FACTORY_3 - With the implementation from this interface, you can register Serializer and Deserializer to transfer complex Java object. The properties for a BeanSerializer is: TypeMappingFactory.PROPERTY_TYPE_MAPPING_FACTORY, BeanTypeMappingFactory.class.getName() + "," + Customer.class.getName() -> [TypeMappingFactory] + , + [BeanClass]
  • REMOTE_URL_AND_PORT (optional) - The server URL and port. Default value is http://localhost:80

Example:

Properties prop = new Properties();
prop(Property.EXECUTOR_CLASS, JaxRpcExecutor.class.getName());
prop(Property.REMOTE_URL_AND_PORT, "http://localhost:8080/axis/services");
prop(TypeMappingFactory.PROPERTY_TYPE_MAPPING_FACTORY, BeanTypeMappingFactory.class.getName() + "," + Customer.class.getName());
prop(TypeMappingFactory.PROPERTY_TYPE_MAPPING_FACTORY_2, BeanTypeMappingFactory.class.getName() + "," + Address.class.getName());

Burlap and Hessian (Caucho)

Properties:

  • STATIC_PROXY_CLASS - The static proxy class: net.sf.crispy.impl.StaticBurlapProxy.
  • REMOTE_URL_AND_PORT (optional) - The server URL and port. Default value is http://localhost:8091/crispy/burlap or http://localhost:8091/crispy/hessian
  • DYNAMIC_PROXY_CLASS - (optional) The dynamic proxy class: Property.VALUE_FOR_JDK_DYNAMIC_PROXY This is necessary, if you want to use a net.sf.crispy.Interceptor or net.sf.crispy.Modifier.

Example:

// load Static-Proxy for Burlap implementation
props.put(Property.STATIC_PROXY_CLASS, StaticBurlapProxy.class.getName());
// url and port from server
prop.put(Property.REMOTE_URL_AND_PORT, "http://localhost:8091/crispy/burlap");


// load Static-Proxy for Hessian implementation
props.put(Property.STATIC_PROXY_CLASS, StaticHessianProxy.class.getName());
// url and port from server
prop.put(Property.REMOTE_URL_AND_PORT, "http://localhost:8091/crispy/hessian");

Hint to Mapping the Servlet (for a hessian example) => the url pattern must end with the *:

<servlet-mapping>
	<servlet-name>crispy-servlet</servlet-name>
	<url-pattern>/crispy/hessian/*</url-pattern>
</servlet-mapping>

JBoss Remoting

Properties:

  • EXECUTOR_CLASS - The executor class: net.sf.crispy.impl.JBossRemotingExecutor.
  • REMOTE_URL_AND_PORT (optional) - The server URL and port. Default value is socket://localhost:5400 (possible protocols are socket, sslsocket, servlet, rmi, http).

Example:

// load Executor for JBoss remoting implementation
props.put(Property.EXECUTOR_CLASS, JBossRemotingExecutor.class.getName());
// url and port from server
prop.put(Property.REMOTE_URL_AND_PORT, "socket://localhost:5400");

CORBA

Properties:

  • STATIC_PROXY_CLASS - The static proxy class: net.sf.crispy.impl.StaticCorbaProxy.
  • REMOTE_URL_AND_PORT (optional) - The server URL and port. Default value is 127.0.0.1:1057

Example:

Properties prop = new Properties();
prop.put(Property.STATIC_PROXY_CLASS, StaticCorbaProxy.class.getName());
// no protocol!
prop.put(Property.REMOTE_URL_AND_PORT, "myhost.de:8081");