8. Miscellaneous Options

Custom Message Interceptor

If you need to do some actions before and after each remote call, such as setting or accessing message headers, or doing some setup before request handling, you can configure a custom AMF3MessageInterceptor in granite-config.xml :

granite-config.xml
<?xml version="1.0" encoding="UTF-8"?>

<granite-config>
   ...
   <amf3-message-interceptor type="com.myapp.MyMessageInterceptor"/>
</granite-config>

When using configuration scanning, you can also put this in META-INF/granite-config.properties of your application jar archive :

amf3MessageInterceptor=com.myapp.MyMessageInterceptor

Take care that some of the GraniteDS server frameworks integration (CDI and Seam) already provide their own message interceptor. If you need to do something else, you will have to override the existing interceptor and call super.before and super.after.

Custom Exception Handler

If you need special service exception handling, either to add extra informations or to mask implementation details, you may configure a custom ServiceExceptionHandler in services-config.xml:

services-config.xml
<?xml version="1.0" encoding="UTF-8"?>

<services-config>
  ...
  <factories>
    <factory id="..." class="...">
      <properties>
        <service-exception-handler>
          path.to.my.CustomServiceExceptionHandler
        </service-exception-handler>
        ...
      </properties>
    </factory>
  </factories>
  ...
</services-config>

Your custom service exception handler must implement the org.granite.messaging.service.ServiceExceptionHandler interface and two methods. Note: It could, of course, extend the org.granite.messaging.service.DefaultServiceExceptionHandler class:

public ServiceException handleNoSuchMethodException(
    Message request,
    Destination destination,
    Object invokee,
    String method,
    Object[] args,
    NoSuchMethodException e
);

public ServiceException handleInvocationException(
    ServiceInvocationContext context,
    Throwable t
);

The first method is called whenever the service invoker cannot find any suitable method with the supplied name and arguments.

The second one is called whenever the method invocation throws an exception. Note that java.lang.reflect.InvocationTargetException are unwrapped (getTargetException) before handleInvocationException is called.

In both cases, the returned ServiceException will be thrown and serialized in a Flex ErrorMessage instead of the raw NoSuchMethodException e or Throwable t one.

Custom Class Getter

A problem with the default AMF3 serialization is to get the true class name of an object in special cases. For example, a simple myObject.getClass().getName() with a proxied entity bean would return org.hibernate.proxy.HibernateProxy instead of the underlying entity bean class name. In order to get through this kind of problem, you must configure a class getter.

For example, here is the full configuration used in the examples/graniteds_ejb3 sample application when the autoscan feature is not used:

granite-config.xml
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE granite-config PUBLIC
    "-//Granite Data Services//DTD granite-config internal//EN"
    "http://www.graniteds.org/public/dtd/2.0.0/granite-config.dtd">

<granite-config>
  <class-getter type="org.granite.hibernate.HibernateClassGetter"/>

  <externalizers>
    <externalizer type="org.granite.hibernate.HibernateExternalizer">
      <include instance-of="test.granite.ejb3.entity.AbstractEntity"/>
    </externalizer>
  </externalizers>
</granite-config>

The org.granite.hibernate.HibernateClassGetter class is used in order to retreive the correct entity class name from a proxy. You may write and plug your own class getter in a similar way.

Custom Java or ActionScript3 Class Descriptors

When a Java object is not Externalizable nor externalized by a GDS externalizer, it is serialized by means of the org.granite.messaging.amf.io.util.DefaultJavaClassDescriptor. This class controls which fields must be serialized and how to retrieve values from those fields.

In similar situations, but at deserialization time, the org.granite.messaging.amf.io.util.DefaultActionScriptClassDescriptor class controls how the corresponding Java object is instantiated and how values are set in this new instance.

You may write and plugin your own Java or ActionScript3 descriptors, for example:

MyJavaClassDescriptor.java
public class MyJavaClassDescriptor
    extends org.granite.messaging.amf.io.util.JavaClassDescriptor {

    public MyJavaClassDescriptor(Class type) {
        super(type);
    }

    @Override
    protected List<Property> introspectProperties() {
        // put your custom code here...
    }
}
MyAS3ClassDescriptor.java
public class MyAS3ClassDescriptor
    extends org.granite.messaging.amf.io.util.ActionScriptClassDescriptor {

    public MyAS3ClassDescriptor(String type, byte encoding) {
        super(type, encoding);
    }

    @Override
    public void defineProperty(String name) {
        // put your custom code here...
    }

    @Override
    public Object newJavaInstance() {
        // put your custom code here...
    }
}

Then, you should put this kind of declaration in your granite-config.xml:

granite-config.xml
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE granite-config PUBLIC
    "-//Granite Data Services//DTD granite-config internal//EN"
    "http://www.graniteds.org/public/dtd/2.0.0/granite-config.dtd">

<granite-config>
    <descriptors>
        <descriptor
            type="path.to.MyClass"
            java="path.to.MyJavaClassDescriptor"
            as3="path.to.MyAS3ClassDescriptor" />
        <descriptor
            instance-of="path.to.MyBaseClass"
            java="path.to.MyJavaClassDescriptor"
            as3="path.to.MyAS3ClassDescriptor" />
        <!-- other descriptor configuration... -->
    </descriptors>
</granite-config>

You must use only one of type or instance-of attributes (i.e., should my descriptor(s) be used for all path.to.MyClass objects, or for all instances of path.to.MyBaseClass), you may use one of, or both, Java or AS3 attributes.

Custom AMF3 (De)Serializers

You may plug your own AMF3 serializer/deserializer.

A custom AMF3 serializer must implement java.io.ObjectOutput and have a special constructor signature:

MyAMF3Serializer.java
public class MyAMF3Serializer implements java.io.ObjectOutput {

    public MyAMF3Serializer(java.io.OutputStream out) {
        // ...
    }

    // ObjectOutput implemention...
}

Then, you must register this serializer in granite-config.xml:

granite-config.xml
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE granite-config PUBLIC
    "-//Granite Data Services//DTD granite-config internal//EN"
    "http://www.graniteds.org/public/dtd/2.0.0/granite-config.dtd">

<granite-config>
    <amf3-serializer type="path.to.MyAMF3Serializer"/>
</granite-config>

A custom AMF3 deserializer must implement java.io.ObjectInput and have a special constructor signature:

MyAMF3Deserializer.java
public class MyAMF3Deserializer implements java.io.ObjectInput {

    public MyAMF3Deserializer(java.io.InputStream in) {
        // ...
    }

    // ObjectInput implemention...
}

Then, you must register this deserializer in granite-config.xml:

granite-config.xml
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE granite-config PUBLIC
    "-//Granite Data Services//DTD granite-config internal//EN"
    "http://www.graniteds.org/public/dtd/2.0.0/granite-config.dtd">

<granite-config>
    <amf3-deserializer type="path.to.MyAMF3Deserializer"/>
</granite-config>

You may of course extend org.granite.messaging.amf.io.AMF3Serializer or org.granite.messaging.amf.io.AMF3Deserializer to override only some parts of the default AMF3 (de)serialization process, as all methods in thoses classes are public or protected.

ServiceInvocationListener (Advanced use only)

If you need to listen to each service invocation method call, you may plugin a org.granite.messaging.service.ServiceInvocationListener implementation like this:

granite-config.xml
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE granite-config PUBLIC
    "-//Granite Data Services//DTD granite-config internal//EN"
    "http://www.graniteds.org/public/dtd/2.0.0/granite-config.dtd">

<granite-config>
    <invocation-listener type="path.to.MyServiceInvocationListener"/>
</granite-config>

Your class must implement the org.granite.messaging.service.ServiceInvocationListener interface and four methods:

public Object[] beforeMethodSearch(Object invokee, String methodName, Object[] args);
public void beforeInvocation(ServiceInvocationContext context);
public void afterInvocationError(ServiceInvocationContext context, Throwable t);
public Object afterInvocation(ServiceInvocationContext context, Object result);
Be very carefull with those listeners as you may break the entire invocation process if you do not return proper args (beforeMethodSearch), if you modify the ServiceInvocationContext (beforeInvocation) or if you return a different object than the service method call result (afterInvocation)!

Browse Space

- Pages
- Blog
- Labels
- Attachments
- Bookmarks
- Mail
- Advanced

Explore Confluence

- Popular Labels
- Notation Guide

Your Account

Log In

Other Features

Add Content

- Add Comment


SourceForge.net Logo
Copyright © 2007-2010 Adequate Systems. All Rights Reserved.