How can I use the different calls of services?
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.
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);
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:
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!");
See details in Developers Guide in section Proxy Pattern.
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 |
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 |
General Properties, independent of the choice of the client technology. All properties are optional and can combinate jointly.
All properties declaretions are Strings!
null
and the transport not supported null values
(XML-RPC for example) you can substitute the null value with this property.
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");
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:
PropertiesLoader loader = new ClassPathPropertiesLoader("example.properties");
PropertiesLoader loader = new ClassPropertiesLoader(this.getClass(), "example.properties");
PropertiesLoader loader = new FilePropertiesLoader("c:/temp/example.properties");
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
In this section i will describe the special properties for the client invocation.
Properties:
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.
Properties:
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).
Crispy know two kinds of RMI invocation:
Static RMI:
Properties:
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:
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).
Properties:
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.
Properties:
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());
Properties:
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>
Properties:
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");
Properties:
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");