Skip to content

spearal/spearal-java

Repository files navigation

Spearal Java

What is Spearal?

Spearal is a compact binary format for exchanging arbitrary complex data between various endpoints such as Java EE, JavaScript / HTML, Android and iOS applications.

Spearal-Java is the common codebase used in all Spearal / Java specific extensions.

How to use the library?

Add the dependency

The library is available in the Maven central repository, so with Maven or Gradle you simply have to reference the dependency on the library in your build system. Note that Javassist is an optional (but necessary to handle partial objects) dependency of the library:

Maven
<dependency>
	<groupId>org.spearal</groupId>
	<artifactId>spearal-java</artifactId>
	<version>${spearal.version}</version>
</dependency>
<dependency>
	<groupId>org.javassist</groupId>
	<artifactId>javassist</artifactId>
	<version>3.18.2-GA</version>
</dependency>
Gradle
compile 'org.spearal:spearal-java:${spearal.version}'
compile 'org.javassist:javassist:3.18.2-GA'

In any other case, just download the Spearal-Java jar from github and add it to your classpath.

Basic usage

First, you need to create a SpearalFactory that contains the central configuration and internal state of the library:

SpearalFactory factory = new DefaultSpearalFactory();

Encoding data is then a matter of creating a new encoder and call the writeAny method:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
SpearalEncoder encoder = factory.newEncoder(baos);
encoder.writeAny(obj);

Decoding is achieved the same way with readAny:

ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
SpearalDecoder decoder = factory.newEncoder(bais);
Object copy = decoder.readAny();

Note that if you know the expected type of the decoded object, you can use the typed version of readAny:

ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
SpearalDecoder decoder = factory.newEncoder(bais);
Invoice copy = decoder.readAny(Invoice.class);

Working with partial encoding:

Spearal lets you encode only a subset of the properties of an object. Let’s say you have bean defined by this class:

class Person implements Serializable {

    private String firstName;
    private String lastName;
    private List<String> phones;

    // getters / setters...
}

If you are retrieving a collection of Persons and just need their first and last names, it is useless to serialize their collections of phones. Spearal lets you encode just the firstName and lastName properties with a property filter:

SpearalFactory factory = new DefaultSpearalFactory();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
SpearalEncoder encoder = factory.newEncoder(baos);

// Only firstName and lastName:
encoder.getPropertyFilter().add(Person.class, "firstName", "lastName");

encoder.writeAny(obj);

When decoding the result, you will get a Javassist proxy for each Person, that will throw a UndefinedPropertyException if you try to access the phones collection:

ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
SpearalDecoder decoder = factory.newEncoder(bais);
Person copy = decoder.readAny(Person.class);

System.out.println(copy.getFirstName());
System.out.println(copy.getLastName());

// This line throws a UndefinedPropertyException:
System.out.println(copy.getPhones());

What is the Spearal Mime Type?

Data exchanged in the Spearal format should use the application/spearal mime type which is defined as the constant Spearal.APPLICATION_SPEARAL.

Configuration

Various elements can be configured in the SpearalFactory. All configurable elements implement the interface Configurable and can be configured with:

spearalFactory.configure(new ConfigurableElement());

AliasStrategy

The first configurable element is the class alias strategy. It is used during serialization and deserialization to convert the name of the class from a local qualified name to the remote qualified name.

The built-in implementation PackageTranslatorAliasStrategy simply translates the package prefix from one name to another. For example with a client package prefix com.mycompany.client and a server package prefix com.mycompany.server, the class name com.mycompany.server.domain.DomainObject will be translated to com.mycompany.client.domain.DomainObject and the corresponding class will be instantiated.

The default does not alias or unalias anything.

Introspector

The role of the introspector is to retrieve (usually by reflection) the list of properties for a specified class. The default IntrospectorImpl is able to instrospect properties of a JavaBean-style class and can be used in most cases. A custom introspector could be necessary for example to deal with JavaFX objects with bindable properties.

PartialObjectFactory

Its role is to instantiate partial objects. When Javassist is present in the classpath, the built-in JavassistPartialObjectFactory is used and creates a Javassist proxy for the class. The default behaviour when Javassist is not present is NoProxyPartialObjectFactory which simply instantiates the target class. Obviously this is not suitable if you really want to use partial objects because they will be sent as 'full' objects from client to server, but with some properties set to null (or default values).

The factory must implement the following method:

/**
 * Create a proxy instance for the target class
 * @param context the current Spearal context
 * @param cls the class to proxy
 * @param partialProperties the defined properties at initialization
 */
public Object instantiatePartial(SpearalContext context, Class<?> cls, Property[] partialProperties);

Securizer

The role of the securizer is to allow or forbid the serialization/deserialization of any particular type. It can be important in particular during deserialization to prevent an attacker to let Spearal instantiate a sensitive class with which it could access the underlying system. The default SecurizerImpl enforces that the target class must implement Serializable but any other policy can be implemented.

The securizer must implement the following method:

void checkDecodable(Type type) throws SecurityException;
void checkEncodable(Class<?> cls) throws SecurityException;

TypeLoader

The role of the loader is to instantiate the target class from its aliased name (or names when multiple interfaces are received). The default TypeLoaderImpl parses the class names, and does the following depending on the cases:

  • If there is only one name, it loads the type. If the type is an interface, it instantiates a Java proxy, else it instantiates the class itself.

  • If the class is not found, it instantiates the class ClassNotFound which is a anonymous object that will contain the deserialized data.

  • If there are many names, it tries to create a Java proxy for all the specified interfaces.

If you need that the classes are loaded in a specific ClassLoader, just reconfigure the default TypeLoaderImpl:

spearalFactory.configure(new TypeLoaderImpl(myClassLoader));

Any other loading policy can be implemented, for example to get object instances from a pool, an external object factory or apply specific transformations or initialization on the instantiated objects.

The loader must implement the following method:

Class<?> loadClass(SpearalContext context, String classNames, Type target);

UnfilterablePropertiesProvider

TODO

How to get and build the project?

$ git clone https://github.com/spearal/spearal-java.git
$ cd spearal-java
$ ./gradlew build

The library can then be found in the build/libs directory.

About

Spearal Java

Resources

License

Stars

Watchers

Forks

Packages

No packages published