Handling Runtime dependencies - java

Dependency injection is a useful technique but what approach is recommended when faced with runtime dependencies?
e.g. Say you want to glue an event to an event processor depending on the type of the event and the user who initiated the request.
public interface Event {}
public interface EventProcessor {
public void handleEvent(Event e);
}
class EventProcessorFactory {
private final User u;
private final Event e;
public EventProcessorFactory(User u, Event e) {
this.u = u;
this.e = e;
}
public EventProcessor get() {
EventProcessor ep;
if(e instanceof LocalEvent) {
ep = new LocalEventProcessor();
}
else if(e instanceof RemoteTriggeredEvent && u instanceof AdminUser) {
//has static dependencies
ep = new RemoteEventProcessor(u);
}
else {
ep = new DefaultEventProcessor();
}
}
}
Now the complexity is encapsulated in the factory, but how else could I achieve the same result, without too much boilerplate code?

As written, what you call 'boilerplate code' looks to me to be just 'code'. You have some processing logic that needs to be stated somewhere (local events go to a local event processor, etc). Trying to avoid explicitly stating that logic could be a mistake.
If it is what you do want to do, the simplest way of doing it is to change the interface to add a method:
boolean isInterestedInEvent(Event e)
then set up all the event processors and loop over them until you find the right one.

You could use something like
public interface EventProcessor {
public boolean supports(Event event, User user);
public void handleEvent(Event event);
}
class EventProcessorFactory {
public void setEventProcessors(List<EventProcessor> processors) {
this.processors = processors;
}
public EventProcessor get(Event event, User user) {
for (EventProcessor processor : processors) {
if (processor.supports(event, user)
return processor;
}
}
}
class LocalEventProcessor implements EventProcessor {
public boolean supports(Event event, User user) {
return (event instanceof LocalEvent);
}
// etc
}
class RemoteEventProcessor implements EventProcessor {
public boolean supports(Event event, User user) {
return (event instanceof RemoteTriggeredEvent) &&
(user instanceof AdminUser);
}
// etc
}
If your processors have some sort of natural ordering, you can implement Comparable to ensure they are tested in the correct order, otherwise you'll can rely on them being injected into the factory in the required order, thus making it configurable.

You could make each Event responsible for creating the EventProcessor. That way you reduce the number of instanceof checks you're required to make and also achieve better encapsulation. Despite being more "OO" the trade-off is that the logic is no longer in one place (i.e. sometimes it's better to just stick with a "procedural approach" as per your example).
public interface Event {
EventProcessor createProcessor(User u);
}
public class RemoteTriggeredEvent implements Event {
public EventProcessor createProcessor(User u) {
return (u instanceof AdminUser) ? new RemoteEventProcessor(u) :
new DefaultEventProcessor();
}
}

Related

Java delegation model to support many Event Types

I have a class which needs to add additional properties based on Event Name. There can be many event names. How to better design this class so that it supports easy enhancement and maintenance in future.
public class MessageDecoratorServiceImpl implements MessageaDecorator {
#Override
public BaseMessage addMessageProperties(BaseMessage inputMessage ){
return decorateMessage(inputMessage);
}
BaseMessage decorateMessage(BaseMessage inputMessage) {
if(inputMessage.getEventName().equals("NewSubscription")) {
decorateSubscription(inputMessage);
}else if( inputMessage.getEventName().equals("NewContact") ) {
decorateNewContact(inputMessage);
}
}
BaseMessage decorateSubscription(BaseMessage inputMessage) {
inputMessage.getProperties().put("customFiled", "customValue");
return inputMessage;
}
BaseMessage decorateNewContact(BaseMessage inputMessage) {
inputMessage.getProperties().put("contactCustomFiled", "value");
return inputMessage;
}
}
You could create a distinct decorator implementation by event type.
It reduces the coupling between them.
public class MessageDecoratorSubscription implements MessageDecorator {
#Override
public BaseMessage addMessageProperties(BaseMessage inputMessage ){
inputMessage.getProperties().put("customFiled", "customValue");
return inputMessage;
}
}
public class MessageDecoratorNewContact implements MessageDecorator {
#Override
public BaseMessage addMessageProperties(BaseMessage inputMessage ){
inputMessage.getProperties().put("contactCustomFiled", "value");
return inputMessage;
}
}
And invoke them as :
new MessageDecoratorSubscription(myBaseMessage);
or
new MessageDecoratorNewContact(myBaseMessage);
Now adding/removing/changing a event processing doesn't affect others.
About these conditional statements :
if(inputMessage.getEventName().equals("NewSubscription")) {
decorateSubscription(inputMessage);
}else if( inputMessage.getEventName().equals("NewContact") ) {
decorateNewContact(inputMessage);
}
It should so be avoided. It is error prone.
You could replace them by a Map<String, MessageDecorator> field with event types as keys and decorator implementations as values.

spring state machine - manage long running processes

I need some advice for using spring state machine for long running processes. I want to design some flow. Let's say I have next states: Start->step1->step2->step3->finish. I have a controller that can send events to the state machine for managing transitions between states. I have a StateMachinePersister. I have a converter from StateMachineContext to byte[] and back. Sounds good for my business goal. So everything should work fine.
But I have a problem? I can't understand how to manage cases when I will decide to change the flow. I mean that if I have a Production environment where some processes persisted in "step2" state. But I am forced to change the flow. let say that I want to add a step or remove a step in the flow. I think I will have problems during state machine deserialization.
So the question is: may be spring state machine is not suitable for me, or there are some recipes how I can manage such cases?
I have an entity which I want to manage states, transitions etc.
#Entity
#Access(AccessType.FIELD)
#Table(name = "processes", indexes = #Index(columnList = "currentState"))
public class Process extends AbstractPersistable<Long> implements ContextEntity<ProcessState, ProcessEvent, Long> { // NOSONAR
private static final long serialVersionUID = 8848887579564649636L;
#JsonIgnore
StateMachineContext<ProcessState, ProcessEvent> stateMachineContext; // NOSONAR
#Enumerated(EnumType.STRING)
ProcessState currentState;
#Override
public void setStateMachineContext(StateMachineContext<ProcessState, ProcessEvent> stateMachineContext) {
if (stateMachineContext == null) {
throw new IllegalStateException("stateMachineContext can't be null");
}
this.currentState = stateMachineContext.getState();
this.stateMachineContext = stateMachineContext;
}
#Override
public StateMachineContext<ProcessState, ProcessEvent> getStateMachineContext() {
return stateMachineContext;
}
...
}
I have StateMachinePersist bean which is responsible for initializing stateMachineContext for particular process.
#Bean
public StateMachinePersist> persist() {
return new StateMachinePersist>() {
#Override
public StateMachineContext<ProcessState, ProcessEvent> read(
ContextEntity<ProcessState, ProcessEvent, Serializable> process) throws Exception {
return process.getStateMachineContext();
}
#Override
public void write(StateMachineContext<ProcessState, ProcessEvent> context,
ContextEntity<ProcessState, ProcessEvent, Serializable> process) throws Exception {
process.setStateMachineContext(context);
}
};
}
I have StateMachineAdapter which is responsible for persisting and restoring state machine
public class DefaultStateMachineAdapter<S, E, T> {
final StateMachineFactory<S, E> stateMachineFactory;
final StateMachinePersister<S, E, T> persister;
public DefaultStateMachineAdapter(StateMachineFactory<S, E> stateMachineFactory, StateMachinePersister<S, E, T> persister) {
this.stateMachineFactory = stateMachineFactory;
this.persister = persister;
}
public StateMachine<S, E> restore(T contextObject) throws Exception {
StateMachine<S, E> stateMachine = stateMachineFactory.getStateMachine();
return persister.restore(stateMachine, contextObject);
}
public void persist(StateMachine<S, E> stateMachine, T order) throws Exception {
persister.persist(stateMachine, order);
}
public StateMachine<S, E> create() {
StateMachine<S, E> stateMachine = stateMachineFactory.getStateMachine();
stateMachine.start();
return stateMachine;
}
}
I have StateMachineContextConverter which is responsible for serialization/deserialization of StateMachineContext. I have used Kryo for this operations.
public class StateMachineContextConverter implements AttributeConverter<StateMachineContext, byte[]> {
#Override
public byte[] convertToDatabaseColumn(StateMachineContext attribute) {
return serialize(attribute);
}
#Override
public StateMachineContext convertToEntityAttribute(byte[] dbData) {
return deserialize(dbData);
}
}
I have controller which is responsible to switch states
public class ProcessEventController {
final DefaultStateMachineAdapter<ProcessState, ProcessEvent, ContextEntity<ProcessState, ProcessEvent, ? extends Serializable>> processStateMachineAdapter;
public ProcessEventController(DefaultStateMachineAdapter<ProcessState, ProcessEvent, ContextEntity<ProcessState, ProcessEvent, ? extends Serializable>> processStateMachineAdapter) {
this.processStateMachineAdapter = processStateMachineAdapter;
}
#RequestMapping(path = "/processes/{id}/{event}", method = RequestMethod.POST)
#Transactional
public HttpEntity<Void> receiveEvent(#PathVariable("id") Process process, #PathVariable("event") ProcessEvent event) throws Exception {
StateMachine<ProcessState, ProcessEvent> stateMachine = processStateMachineAdapter.restore(process);
if (stateMachine.sendEvent(event)) {
processStateMachineAdapter.persist(stateMachine, process);
return ResponseEntity.accepted().build();
} else {
return ResponseEntity.unprocessableEntity().build();
}
}
}
Obviously you can't modify existing running machine but as you're already working with persistence I assume you're at least stopping machines.
Look statemachine-examples-datajpa which is using existing machine apis to store config in a DB. We have StateMachineModelFactory which pretty much allows you to store your stuff anywhere if any build-in implementation is not suitable for you. This sample will build new machine instances every time when new events are sent. i.e. you can go to a DB using build-in editor and add new transition(s) without restarting a main java process.
This gives you some flexibility but if you need to change your running code then things will get impossible.

Android EventBus use singletone class to handle all events

in this below class i try to handle all events on application. i dont like to write some class to handle theme, in EventBus documentation, that use class constructor, my class must be singleton to be has public, then i wrote simple class as :
public class SignalEvents {
private boolean internetConnectionState;
private boolean activityMarketDetailState;
public boolean isInternetConnectionState() {
return internetConnectionState;
}
public void setInternetConnectionState(boolean internetConnectionState) {
this.internetConnectionState = internetConnectionState;
}
public boolean isActivityMarketDetailState() {
return activityMarketDetailState;
}
public void setActivityMarketDetailState(boolean activityMarketDetailState) {
this.activityMarketDetailState = activityMarketDetailState;
}
}
now, for eventBus and send event i try to use:
SignalEvents signal = new SignalEvents();
EventBus.getDefault().post(signal.setActivityMarketDetailState(true));
but then i get error :
Error:(98, 67) error: 'void' type not allowed here
You need to post objects. For example:
EventBus.getDefault().post(new ActivityMarketDetailStateChanged(true));
And have such a class:
public class ActivityMarketDetailStateChanged {
public final boolean newState;
public ActivityMarketDetailStateChanged(boolean newState) {
this.newState = newState;
}
}
And then register to subscribe to those events, depending on if you are using EventBus 2.x or 3.x, assuming version 3:
EventBus.getDefault().register(signal);
Need a subscribe method to receive it in your SignalEvents class:
#Subscribe
public void onEvent(ActivityMarketDetailStateChanged event) {
setActivityMarketDetailState(event.newState);
}

Java Observers/Object Listeners (Game Engine)

I'm working on a game engine, and the last question I had regarding this was what good way I can use to make "observers" or listeners. A user suggested that I should use Java's EventObject class to inherit from and make a Listener interface. However, this didn't provide me with good flexibility.
Here is the Handler annotation to state that a method is an event handler in a listener:
#Retention(RetentionPolicy.CLASS)
#Target(ElementType.METHOD)
public #interface Handler {}
Here is the base class for Event, which is basically the same as EventObject (but I'll add abstract methods sooner or later):
public abstract class Event {
private Object source;
public Event(Object source) {
this.source = source;
}
public Object getSource() {
return source;
}
}
Here is the Listener class, which is empty:
public interface Listener {}
Here is the ListenerHandler class, used to handle all listeners. You register and unregister them here. I'll edit the register/unregister methods later for a better use:
public class ListenerHandler {
private ArrayList<Listener> listeners;
public ListenerHandler() {
this.listeners = new ArrayList<Listener>();
}
public void registerListener(Listener l) {
listeners.add(l);
}
public void unregisterListener(Listener l) {
listeners.remove(l);
}
public void onEvent(Event event) {
for(Listener l : listeners) {
Class<?> c = l.getClass();
Method[] methods = c.getDeclaredMethods();
for(Method m : methods) {
if(m.isAccessible()) {
if(m.isAnnotationPresent(Handler.class)) {
Class<?>[] params = m.getParameterTypes();
if(params.length > 1) {
continue;
}
Class<?> par = params[0];
if(par.getSuperclass().equals(Event.class)) {
try {
m.invoke(this, event);
}catch(IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
}
}
}
From what I heard, it's a use of a lot of memory in order to get all methods of a class. I'm not going to assume this is the case, but I'm sure there is a better way as this will be a game engine with many components and such.
I'd like to know the best way to implement this, or if I'm doing it right. I'd also like to know if anyone can help me improve this in any way without hogging memory usage by the game (as of now it's not a big deal -- the "game engine" is not even close to rendering anything yet)
I tried to keep it a very simple example and will comment with different ideas to it:
First meet the Achievement class:
import java.util.Observable;
public class Achievement extends Observable {
public static class AchievementDetails {}
public Achievement() {
addObserver(EventsListener.getInstance());
}
public void achievementReached() {
AchievementDetails achievemetDetails = null;
setChanged();
notifyObservers(achievemetDetails);
}
}
And then the events listener class:
import com.test.Achievement.AchievementDetails;
public class EventsListener implements Observer {
private static EventsListener instance = new EventsListener();
public static EventsListener getInstance() {
return instance;
}
#Override
public void update(Observable o, Object arg) {
if(o instanceof Achievement) {
AchievementDetails achievemetDetails = (AchievementDetails) arg;
//do some logic here
}
}
}
The only one thing that is missing is to create an instance of your achievement (which register the EventsListener to itself) and handle the life cycle of it.

How to capture revision event of WTObject?

I am using Windchill 10.0 M030. I have created a windchill service that captures some actions. I am done with capturing the delete, checkin, and state change events, but I don't know how to capture the revision event of an object. Can someone help me out?
Some example code snippets would be helpful. The events that are working fine are the following:
public void notifyEvent(KeyedEvent event) throws RemoteException,
WTException {
if (event instanceof PersistenceManagerEvent) {
notifyEvent((PersistenceManagerEvent) event);
}
if (event instanceof WorkInProgressServiceEvent) {
notifyEvent((WorkInProgressServiceEvent) event);
}
if (event instanceof EPMWorkspaceManagerEvent) {
notifyEvent((EPMWorkspaceManagerEvent) event);
}
if (event instanceof LifeCycleServiceEvent) {
notifyEvent((LifeCycleServiceEvent) event);
}
}
Is there any separate event like Revise event to be captured in this way? How can I do that?
Thank you.
Here is the code for your ListenerAdapter :
public class VersionEventListenerAdapter extends ServiceEventListenerAdapter {
public VersionEventListenerAdapter(String serviceId) {
super(serviceId);
}
public void notifyVetoableEvent(Object event) throws WTException, WTPropertyVetoException {
if (!(event instanceof KeyedEvent)) {
return;
}
Object target = ((KeyedEvent) event).getEventTarget();
Object eventType = ((KeyedEvent) event).getEventType();
if (eventType.equals(VersionControlServiceEvent.NEW_VERSION)
{
/** Call your business code here
example : yourMethod(target);
**/
}
}
And then the service to register the listener
public class MyStandardListenerService extends StandardManager implements MyListenerServiceInterface {
private static final long serialVersionUID = 1L;
protected synchronized void performStartupProcess() throws ManagerException {
VersionEventListenerAdapter versionEventListenerAdapter = new VersionEventListenerAdapter(getName());
getManagerService().addEventListener(versionEventListenerAdapter, VersionControlServiceEvent.generateEventKey(VersionControlServiceEvent.NEW_VERSION));
}
public static MyStandardListenerService newMyStandardListenerService() throws WTException {
MyStandardListenerService instance = new MyStandardListenerService();
instance.initialize();
return instance;
}
This new service need to be registered in the wt.properties. See the customizer's guide for more details about how to register it (with xconfmanager command line utility)

Categories