Jackson Mixin To The Rescue

Many a times it is not possible to annotate classes with Jackson Annotations simply for serialization/deserialization needs. There could be many reasons, for example

  • Classes which needs to be serialized/deserialized are 3rd party classes.
  • You don’t want Jackson invade into your code base every where.
  • You want cleaner and modular design.

Jackson Mixin feature would help solve above problems easily. Lets consider an example :

Let’s say you want to serialize/deserialize following class (Note that it does not have getter/setter)

 public class Address {

	private String city;
	private String state;

	public Address(String city, String state) {
		this.city = city;
		this.state = state;;
	}

	@Override
	public String toString() {
		return "Address [city=" + city + ", state=" + state +  "]";
	}
}

If you try to serialize, you would get the following error

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class com.some.package.Address and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) )

To solve the above issue, you have to do the following

  1. Add a default constructor
  2. Add getter/setter for each property

However that is not possible for many cases. Here we can use Jackson Mixin to get around with this problem, to do that we have to create corresponding mixing class, as can be seen here, constructor should match as that of your source object and you have to use Jackson annotations (@JsonCreator, @JsonProperty etc.)

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public abstract class AddressMixin {

    @JsonCreator
    public AddressMixin(
            @JsonProperty("city") String city,
            @JsonProperty("state") String state) {
        System.out.println("Wont be called");
        
    }
}

Still you will get the following exception

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class com.some.package.Address and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) )

It turns out that, we have to tell jackson to use reflection and access the fields.

mapper.setVisibility(mapper.getSerializationConfig()
        	.getDefaultVisibilityChecker()
                .withFieldVisibility(JsonAutoDetect.Visibility.ANY)
                .withGetterVisibility(JsonAutoDetect.Visibility.NONE)
                .withSetterVisibility(JsonAutoDetect.Visibility.NONE)
                .withCreatorVisibility(JsonAutoDetect.Visibility.NONE));

Here is the teset code:

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonMixInTest {

	public static void main(String[] args) throws IOException {
   
	Address address = new Address("Hyderabad",  "Telangana");

        ObjectMapper mapper = buildMapper();

        final String json = mapper.writeValueAsString(address);
        System.out.println(json);

        mapper.addMixIn(Address.class, AddressMixin.class);

        final Address deserializedUser = mapper.readValue(json, Address.class);
        System.out.println(deserializedUser);
    }

	private static ObjectMapper buildMapper() {
		ObjectMapper mapper = new ObjectMapper();
                mapper.setVisibility(mapper.getSerializationConfig()
                .getDefaultVisibilityChecker()
                .withFieldVisibility(JsonAutoDetect.Visibility.ANY)
                .withGetterVisibility(JsonAutoDetect.Visibility.NONE)
                .withSetterVisibility(JsonAutoDetect.Visibility.NONE)
                .withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
		return mapper;
	}
}

Here is the output:

{"city":"Hyderabad","state":"Telangana"}
Address [city=Hyderabad, state=Telangana]

References

 

Advertisements

Running Multiple Zookeeper Instances On Single Windows Machine

This is indeed correct with Zookeeper Runner

Running Zookeeper in windows in made so easy that even grandma can do it.

 Step 1: copy three instances of ZookeeperRunner on your local drive

I have copied it as instance1, instance2 and instance3

zookeeper-multi-instances

Step 2: copy required zookeeper jar files to [RUNNER_HOME]/lib

Note : you have to do it for all the instances.

zookeeper-runner-jars

Step 3: update the zoo config files

Note: you have to do it for all the instances.

client ports

instance1= 2181

instance2=2182

instance3=2183

zookeeper-runner-zoo-config

Step 4: update the wrapper config

zookeeper-runner-wrapper-config

Step 5 : update the myid files

zookeeper-runner-myid

Step 6 : start Zookeeper instances

Note : you have to do it for all the instances.

zookeeper-runner-start

Here are the running instances.

zookeeper-runner-instance1

zookeeper-runner-instance2

zookeeper-runner-instance3

 

Multi Node Distributed Execution Using Infinispan and Dexecutor

We will try to execute Dexecutor in a distributed mode using Infinispan. For the demo we would be setting up multiple infinispan nodes on single machine.

Refer Introducing Dexecutor, to get an introduction on Dexecutor  and to understand the problem we would solve in a distribute fashion. In short:

We would be distributing the execution of dexecutor tasks on Infinispan nodes in a single machine.

To do that one of the nodes would act as master and submit the tasks to DistributedExecutorService to be executed by other infinispan worker nodes.

Step 1: Add dexecutor-infinispan dependency

 


<dependency>
  <groupId>com.github.dexecutor</groupId>
  <artifactId>dexecutor-core</artifactId>
 <version>1.0.2</version>
</dependency>

 

Step 2: Add the default jgroups.xml

Step 3: Create the CacheManager

private DefaultCacheManager createCacheManagerProgrammatically(final String nodeName, final String cacheName) {
	DefaultCacheManager cacheManager = new DefaultCacheManager(globalConfiguration(nodeName), defaultConfiguration());
	cacheManager.defineConfiguration(cacheName, cacheConfiguration());
	return cacheManager;
}

private GlobalConfiguration globalConfiguration(String nodeName) {
	return GlobalConfigurationBuilder
				.defaultClusteredBuilder()
				.transport()
				.nodeName(nodeName)
				.addProperty("configurationFile", "jgroups.xml")
				.build();
}

private Configuration defaultConfiguration() {
	return new ConfigurationBuilder()
				.clustering()
				.cacheMode(CacheMode.REPL_SYNC)
				.build();
}

private Configuration cacheConfiguration() {
	return new ConfigurationBuilder()
				.clustering()
				.cacheMode(CacheMode.DIST_SYNC)
				.hash()
				.numOwners(2)
				.build();
}

Step 4 : Create Dexecutor instance using InfinispanExecutionEngine


EmbeddedCacheManager cacheManager = createCacheManagerProgrammatically(nodeName, cacheName);
final Cache<String, String> cache = cacheManager.getCache(cacheName);
DefaultExecutorService distributedExecutorService = new DefaultExecutorService(cache);
DefaultDependentTasksExecutor<Integer, Integer> dexecutor = newTaskExecutor(distributedExecutorService);

private DefaultDependentTasksExecutor<Integer, Integer> newTaskExecutor(final DistributedExecutorService executorService) {
	return new DefaultDependentTasksExecutor<Integer, Integer>(taskExecutorConfig(executorService));
}

private DependentTasksExecutorConfig<Integer, Integer> taskExecutorConfig(final DistributedExecutorService executorService) {
	return new DependentTasksExecutorConfig<Integer, Integer>(executionEngine(executorService), new SleepyTaskProvider());
}

private InfinispanExecutionEngine<Integer, Integer> executionEngine(final DistributedExecutorService executorService) {
	return new InfinispanExecutionEngine<Integer, Integer>(executorService);
}

Step 5: Only master should create tasks

if (isMaster) {
	DefaultExecutorService distributedExecutorService = new DefaultExecutorService(cache);
	DefaultDependentTasksExecutor<Integer, Integer> dexecutor = newTaskExecutor(distributedExecutorService);

	buildGraph(dexecutor);
	dexecutor.execute(ExecutionBehavior.TERMINATING);
}

Refer the full code here

Step 4: Run the Application

Terminal #1 : run as worker

mvn test-compile exec:java -Djava.net.preferIPv4Stack=true -Dexec.mainClass="com.github.dexecutor.infinispan.Node" -Dexec.classpathScope="test" -Dexec.args="s node-A"

Terminal #2: run as worker

mvn test-compile exec:java -Djava.net.preferIPv4Stack=true -Dexec.mainClass="com.github.dexecutor.infinispan.Node" -Dexec.classpathScope="test" -Dexec.args="s node-B"

 

Terminal #3 : run as master

mvn test-compile exec:java  -Dexec.classpathScope="test" -Djava.net.preferIPv4Stack=true -Dexec.mainClass="com.github.dexecutor.infinispan.Node" -Dexec.args="m node-C"

Here is the output:

dexecutor-multi-node-infinispan-single-machine

References

Introducing Dexecutor

From the Dexecutor Website

Executing dependent/Independent tasks in a reliable way, is made so easy that even grandma can do it.

That is indeed true with Dexecutor, specially considering the complexity involved writing error free programs involving dependent/Independent tasks. Without Dexecutor, you would end up writing tons of plumbing code rather than concentrating on the business.

With Dexecutor, you model your requirements in terms of Graph in an object oriented way, and rest would be taken care by the framework in a reliable way. Dynamically built graph, defines the executing order, what are all tasks that should run in parallel/sequential. For example if the graph built is the following

dexecutor_graph

Then it means, Task#1,Task#12 and Task#11 would run in parallel, once one of them finishes execution, its child nodes would begin execution. For example lets say if Task#1 finishes, then Task#2 and Task#3 would begin, similarly with Task#12 and Task#11, until all the tasks are executed or if a task end up in an error (If the execution behaviour is terminating)

That’s great indeed…. But how it is done ?

 DefaultDependentTasksExecutor<Integer, Integer> executor = newTaskExecutor();

//Build the graph
executor.addDependency(1, 2);
executor.addDependency(1, 3);
executor.addDependency(3, 4);
executor.addDependency(3, 5);
executor.addDependency(3, 6);
//executor.addDependency(10, 2); // cycle
executor.addDependency(2, 7);
executor.addDependency(2, 9);
executor.addDependency(2, 8);
executor.addDependency(9, 10);
executor.addDependency(12, 13);
executor.addDependency(13, 4);
executor.addDependency(13, 14);
executor.addIndependent(11);

// Execute
executor.execute(ExecutionBehavior.NON_TERMINATING);

Above code shows, Dexecutor expose two kind of APIs,

  • An API to construct the Graph (addDependency, addIndependent)
  • An API to Start execution (execute)

That’s simple indeed, You may be wondering, How the tasks are mapped?

TaskProvider to the rescue. TaskProviders, maps a graph node to a task, and is basically provided during Dexecutor instance creation. Refer to JavaDoc of Dexecutor Implementation, and an example of how to do it.

Well, the example is very simple, Can we get any real time example of how Dexecutor can be used?

Yes indeed, Dexecutor does have a sample application which provides a real time scenario, refer it for more details.

Finally I would like to quote the features of Dexecutor, Here is a snapshot from the website.

dexecutor-features

Refrences