All pages
Powered by GitBook
1 of 3

Loading...

Loading...

Loading...

Wrapping

This is the manual for older MicroStream versions (Version < 5.0).

The new documentation (Version >= 5.0) is located at:

https://docs.microstream.one/

MicroStream uses a strictly interface-based architecture. All types in the public API are, whenever possible, interfaces. This offers the best possibilities to extend or exchange parts of the engine. A good ways to enrich a type with features, is the wrapper (decorator) pattern.

For example, let's say we want to add logging to the PersistenceStoring's store(object) method.

PersistenceStoring.java
public interface PersistenceStoring
{
    public long store(Object instance);
    
    public long[] storeAll(Object... instances);
    
    public void storeAll(Iterable<?> instances);
    
    public void storeSelfStoring(SelfStoring storing);
}

Conventionally it would be done that way: A new type, implementing the original interface, would be handed over the wrapped instance, all interface methods have to be implemented and delegated. And in the single method, we wanted to add functionality; the actual implementation of the logging is done.

public class PersistenceStoringWithLogging implements PersistenceStoring
{
	private final PersistenceStoring wrapped;
	
	public PersistenceStoringWithLogging(final PersistenceStoring wrapped)
	{
		super();
		
		this.wrapped = wrapped;
	}
	
	@Override
	public long store(final Object instance)
	{
		Logger.getLogger(PersistenceStoring.class.getName())
			.info("Object stored: " + instance);
		
		return this.wrapped.store(instance);
	}
	
	@Override
	public long[] storeAll(final Object... instances)
	{
		return this.wrapped.storeAll(instances);
	}
	
	@Override
	public void storeAll(final Iterable<?> instances)
	{
		this.wrapped.storeAll(instances);
	}
	
	@Override
	public void storeSelfStoring(final SelfStoring storing)
	{
		this.wrapped.storeSelfStoring(storing);
	}
}

This produces a lot of overhead. In this case, three methods are just boilerplate code to delegate the calls to the wrapped instance. A common solution for that is to create an abstract base wrapper type for the designated interface, and to reuse it whenever needed.

public abstract class BaseWrapperPersistenceStoring implements PersistenceStoring
{
	private final PersistenceStoring wrapped;
	
	public BaseWrapperPersistenceStoring(final PersistenceStoring wrapped)
	{
		super();
		
		this.wrapped = wrapped;
	}
	
	@Override
	public long store(final Object instance)
	{
		return this.wrapped.store(instance);
	}
	
	@Override
	public long[] storeAll(final Object... instances)
	{
		return this.wrapped.storeAll(instances);
	}
	
	@Override
	public void storeAll(final Iterable<?> instances)
	{
		this.wrapped.storeAll(instances);
	}
	
	@Override
	public void storeSelfStoring(final SelfStoring storing)
	{
		this.wrapped.storeSelfStoring(storing);
	}
}

And then, based on that, the implementation of the logger type would look like this:

public class PersistenceStoringWithLogging extends BaseWrapperPersistenceStoring
{
	public PersistenceStoringWithLogging(PersistenceStoring wrapped)
	{
		super(wrapped);
	}
	
	@Override
	public long store(Object instance)
	{
		Logger.getLogger(PersistenceStoring.class.getName())
			.info("Object stored: " + instance);
			
		return super.store(instance);
	}
}

That's better. No more boilerplate code. Just overwrite the methods you want to extend.

The only work left is, to generate the base wrapper types. One way is to let your IDE generate the wrapper or delegation code. Disadvantage of that is, it has to be redone every time your interfaces change. A code generator, which does it automatically would be nice. And that's what the base module brings along. Like the layered entity code generator, it is an annotation processor.

Usage

This is the manual for older MicroStream versions (Version < 5.0).

The new documentation (Version >= 5.0) is located at:

MicroStream's wrapper code generator generates following wrapper type for PersistenceStoring:

It is not an abstract class, but an interface, which extends the Wrapper interface of the base module, and the wrapped type itself. This offers you the most flexible way to use it in your application.

The Wrapper type is just a typed interface and an abstract implementation of itself.

You can either implement the Wrapper interface and provide the wrapped instance via the wrapped() method, or you can extend the abstract class and hand over the wrapped instance to the super constructor.

Version with the abstract type:

Or only the interface, then you have to provide the wrapped instance via wrapped():

public interface WrapperPersistenceStoring 
	extends Wrapper<PersistenceStoring>, PersistenceStoring
{
	@Override
	public default long store(final Object instance)
	{
		return this.wrapped().store(instance);
	}

	@Override
	public default long[] storeAll(final Object... instances)
	{
		return this.wrapped().storeAll(instances);
	}

	@Override
	public default void storeAll(final Iterable<?> instances)
	{
		this.wrapped().storeAll(instances);
	}

	@Override
	public default void storeSelfStoring(final SelfStoring storing)
	{
		this.wrapped().storeSelfStoring(storing);
	}
}
Wrapper.java
public interface Wrapper<W>
{
	public W wrapped();
	
	public abstract class Abstract<W> implements Wrapper<W>
	{
		private final W wrapped;

		protected Abstract(final W wrapped)
		{
			super();
			
			this.wrapped = wrapped;
		}
		
		@Override
		public final W wrapped()
		{
			return this.wrapped;
		}
	}
}
public class PersistenceStoringWithLogging 
	extends Wrapper.Abstract<PersistenceStoring>
	implements WrapperPersistenceStoring
{
	public PersistenceStoringWithLogging(final PersistenceStoring wrapped)
	{
		super(wrapped);
	}
	
	@Override
	public long store(Object instance)
	{
		Logger.getLogger(PersistenceStoring.class.getName())
			.info("Object stored: " + instance);
		
		return WrapperPersistenceStoring.super.store(instance);
	}
}
public class PersistenceStoringWithLogging 
	implements WrapperPersistenceStoring
{
	private final PersistenceStoring wrapped;

	public PersistenceStoringWithLogging(final PersistenceStoring wrapped)
	{
		super();
		
		this.wrapped = wrapped;
	}
	
	@Override
	public PersistenceStoring wrapped()
	{
		return this.wrapped;
	}
	
	@Override
	public long store(Object instance)
	{
		Logger.getLogger(PersistenceStoring.class.getName())
			.info("Object stored: " + instance);
		
		return WrapperPersistenceStoring.super.store(instance);
	}
}
https://docs.microstream.one/

Configuration

This is the manual for older MicroStream versions (Version < 5.0).

The new documentation (Version >= 5.0) is located at:

https://docs.microstream.one/

The wrapper code generator is an annotation processor, provided by the base module.

The maven configuration looks like this:

<dependencies>
	<dependency>
		<groupId>one.microstream</groupId>
		<artifactId>base</artifactId>
		<version>02.01.00-MS-GA</version>
	</dependency>
</dependencies>

<build>
	<plugins>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-compiler-plugin</artifactId>
			<version>3.8.1</version>
			<configuration>
				<source>1.8</source>
				<target>1.8</target>
				<encoding>UTF-8</encoding>
				<annotationProcessors>
					<annotationProcessor>one.microstream.wrapping.codegen.WrapperProcessor</annotationProcessor>
				</annotationProcessors>
				<compilerArgs>
					<arg>-Amicrostream.wrapper.types=one.microstream.persistence.types.PersistenceStoring</arg>
				</compilerArgs>
			</configuration>
		</plugin>
	</plugins>
</build>

There are following ways to get the base wrapper types generated. If you want it for your own types, the best way is to use the GenerateWrapper annotation.

@GenerateWrapper
public interface MyInterface
{
	public void doStuff();
	
	public String getStuff();
}

Or, if you want it for interfaces in libraries, like PersistenceStoring, you cannot add an annotation. That's what the microstream.wrapper.types parameter is for. This is just a comma separated list of types. Alternatively you can use the GenerateWrapperFor annotation:

@GenerateWrapperFor("one.microstream.persistence.types.PersistenceStoring")
public class WrapperGenerationDummy
{
}

It accepts a list of type names. Plain strings have to be used instead of class literals, because it is read inside the compilation cycle which prohibits access to class elements.