org.zdevra.sparrow.AbstractSparrowEngine Maven / Gradle / Ivy
/*****************************************************************************
* Copyright 2012 Zdenko Vrabel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*****************************************************************************/
package org.zdevra.sparrow;
import org.zdevra.sparrow.actor.*;
import org.zdevra.sparrow.adapter.AbstractAdapterBuilder;
import org.zdevra.sparrow.adapter.AdapterBuilder;
import org.zdevra.sparrow.adapter.InboundAdapter;
import org.zdevra.sparrow.aggregator.AggregatorBuilder;
import org.zdevra.sparrow.aggregator.AggregatorBuilderImpl;
import org.zdevra.sparrow.chain.ChainBuilder;
import org.zdevra.sparrow.channel.*;
import org.zdevra.sparrow.dsl.FromDsl;
import org.zdevra.sparrow.dsl.SparrowDsl;
import org.zdevra.sparrow.filter.FilterBuilder;
import org.zdevra.sparrow.filter.HeaderFilterRuleBuilder;
import org.zdevra.sparrow.filter.PayloadFilterRuleBuilder;
import org.zdevra.sparrow.smartproxy.BaseSmartProxyBuilder;
import org.zdevra.sparrow.smartproxy.SmartProxy;
import org.zdevra.sparrow.smartproxy.SmartProxyBuilder;
import org.zdevra.sparrow.splitter.SplitterBuilder;
import org.zdevra.sparrow.splitter.SplitterBuilderImpl;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* Abstract sparrow engine could be used as standalone engine without any dependencies to Guice or anything else.
* What you need to do is just extend this class, implement the {@link org.zdevra.sparrow.AbstractSparrowEngine#configureSparrow()}
* method where you place your own sparrow configuration of message system.
*
*
* AbstractSparrowEngine has some limitations. First of all is no dependency injection what means that annotations like
* ChannelNamed etc doesn't work. Also you could create actors/channels only from instances, not from classes.
*
*
*
How to configure/build/use your own messaging system
*
*
*
* public class MyMessagingSystem extends AbstractSparrowEngine
* {
* public void configureSparrow() {
* ...
* channel("fib-ch").asInstance(new DirectChannel());
*
* actor("fibonacci-1")
* .input("fib-ch")
* .output("log-ch")
* .actorOf(new FibonacciActor());
*
* channel("log-ch").asInstance(new DirectChannel());
* ...
* }
* }
*
*
* The engine's implementation above build simple actor that computes fibonacci numbers for messages with input and
* output channel. In {@code configureSparrow()} method you could use any of entities that Sparrow offers you like
* {@link org.zdevra.sparrow.splitter.SplitterActorHandler splitters}, {@link org.zdevra.sparrow.aggregator.AggregatorActorHandler aggregators}
* etc. via protected methods.
*
*
* If you have defined you messaging system, you will want start it and use it then.
*
*
*
* SparrowEngine engine = new MyMessagingSystem();
* engine.start();
* ...
* MessageChannel ch = engine.getMessageChannel("fib-ch");
* ch.send(message);
* ...
* engine.shutdown();
*
*
* Example code prepare, initialize and start all entities defined in {@code MyMessagingSystem} implementation. You
* could access to any channel defined inside system and as you can see in example, you cold send the message into
* system through this channel. At the end the engine goes shutdown.
*
*
* Supported entities by Sparrow
*
*
* Sparrow follows similar style of configuration as is used in Guice. There are several builders and methods that
* creates concrete entities like channels, actors etc.
*
*
* channel("input").asInstance(new LoadBalancerChannel());
*
* This statement creates load balanced channel named as 'input'. Into method {@code asInstance} you could forward
* any message implementation you want. Sparrow engine then bind the concrete instance of channel with channel's name.
*
*
*
* ActorHandler handler = ...;
* actor("act1")
* .input("input-ch")
* .mailbox(new FifoMailbox(10))
* .actorOf(handler);
*
* This code fragment creates actor 'act1' that receive messages from 'input-ch' and with processing procedure
* implemented in {@code handler}. InputOutputActor is using different implementation of mailbox that is {@code FifoMailbox}
* with capacity 10. Because handler isn't extending {@link org.zdevra.sparrow.actor.OutputActorHandler}, you don't
* need specify output channel.
*
*
*
* @author Zdenko Vrabel ([email protected])
*/
public abstract class AbstractSparrowEngine implements SparrowEngine, SparrowDsl
{
//-------------------------------------------------------------------------------------------------------------------
// members
//-------------------------------------------------------------------------------------------------------------------
/** collection of all channels with names */
private final Map channels;
/** collection of all units */
private final List units;
/** temporary list of all other builders. Exists during 'configureSparrow()' calling */
private List preBuilders;
/** temporary list of all channels builders. Exists during 'configureSparrow()' calling */
private List channelBuilders;
/** temporary list of all actor builders. Exists during 'configureSparrow()' calling */
private List actorBuilders;
//-------------------------------------------------------------------------------------------------------------------
// constructor
//-------------------------------------------------------------------------------------------------------------------
/**
* Constructor
*/
public AbstractSparrowEngine()
{
channels = new HashMap();
units = new LinkedList();
}
//-------------------------------------------------------------------------------------------------------------------
// abstract & protected methods
//-------------------------------------------------------------------------------------------------------------------
/**
* Here you will place configuration of your message system.
*/
public abstract void configureSparrow();
/**
* Create/register new actor into engine's context
*
* @return
*/
@Override
public ActorBuilder actor(ActorHandlerBuilder actor)
{
return new StandaloneActorBuilder(actor);
}
/**
* Create/register new actor into engine's context
*
* @return
*/
@Override
public ActorBuilder actor(ActorHandler> actor)
{
return new StandaloneActorBuilder(new InstanceActorHandlerBuilder(actor));
}
/**
* Create/register new actor into engine's context
*
* @return
*/
@Override
public ActorBuilder actor(Class extends ActorHandler> actor)
{
return new StandaloneActorBuilder(new ClassActorHandlerBuilder(actor));
}
/**
* Create/register new channel into engine's context
*
* @return
*/
@Override
public ChannelBuilder channel(String channelName)
{
ChannelBuilder b = new StandaloneChannelBuilder();
b.namedAs(channelName);
channelBuilders.add(b);
return b;
}
/**
* Create reference to channel
*
* @param name
* @return
*/
@Override
public ChannelReference channelRef(String name)
{
return new NamedChannelReference(name);
}
/**
* Create/register new inbound adapter into engine's context
*
* @return
*/
@Override
public AdapterBuilder inboundAdapter()
{
AdapterBuilder b = new StandaloneAdapterBuilder();
return b;
}
/**
* Create/register smart {@link SmartProxy proxy} which is more complex entity.
*
* @param name
* name for smart proxy
*/
@Override
public SmartProxyBuilder smartProxy(String name)
{
BaseSmartProxyBuilder b = new BaseSmartProxyBuilder(name, this);
preBuilders.add(b);
return b;
}
/**
* builds {@link org.zdevra.sparrow.aggregator.AggregatorStrategy aggregator}
*
* @return aggregator builder
*/
@Override
public AggregatorBuilder aggregator()
{
return new AggregatorBuilderImpl();
}
/**
* builds {@link org.zdevra.sparrow.splitter.SplitterStrategy splitter}
*
* @return aggregator builder
*/
@Override
public SplitterBuilder splitter()
{
return new SplitterBuilderImpl();
}
/**
* Builds filter that can be used as actor
* @return
*/
@Override
public FilterBuilder filter()
{
return new FilterBuilder();
}
/**
* Builds filter-rule for {@code filter()} that filter message where condition is
* header data
*
* @param header
* @return
*/
@Override
public HeaderFilterRuleBuilder header(String header)
{
return new HeaderFilterRuleBuilder(header);
}
/**
* Builds filter-rule for {@code filter()} that filter message where condition is
* payload data
*
* @return
*/
@Override
public PayloadFilterRuleBuilder payload()
{
return new PayloadFilterRuleBuilder();
}
@Override
public FromDsl from(ChannelReference channel)
{
FromDslImpl fromBuilder = new FromDslImpl();
fromBuilder.setInputChannel(channel);
return fromBuilder;
}
//-------------------------------------------------------------------------------------------------------------------
// inner builders
//-------------------------------------------------------------------------------------------------------------------
/**
* Implementation of {@link org.zdevra.sparrow.dsl.FromDsl} for standalone engine
*/
private class FromDslImpl extends FromDsl
{
@Override
public ChainBuilder chain(String name)
{
ChainBuilder b = new ChainBuilder(this.inputChannelRef, name, AbstractSparrowEngine.this);
preBuilders.add(b);
return b;
}
@Override
public ActorBuilder actor(ActorHandler> actor)
{
ActorBuilder b = new StandaloneActorBuilder(new InstanceActorHandlerBuilder(actor), this.inputChannelRef);
return b;
}
@Override
public ActorBuilder actor(ActorHandlerBuilder actor)
{
ActorBuilder b = new StandaloneActorBuilder(actor, this.inputChannelRef);
return b;
}
@Override
public ActorBuilder actor(Class extends ActorHandler> actor)
{
ActorBuilder b = new StandaloneActorBuilder(new ClassActorHandlerBuilder(actor), this.inputChannelRef);
return b;
}
}
/**
* Implementation of {@link AdapterBuilder} for standalone engine
*/
private class StandaloneAdapterBuilder extends AbstractAdapterBuilder
{
@Override
public void asInstance(InboundAdapter adapter)
{
adapter.setOutputName(this.outputName);
AbstractSparrowEngine.this.units.add(adapter);
}
@Override
public void asClass(Class extends InboundAdapter> adapterClass)
{
throw new UnsupportedOperationException("asClass is supported only for Guice");
}
}
/**
* Implementation of {@link ActorBuilder} for standalone engine
*/
private class StandaloneActorBuilder extends AbstractActorBuilder
{
/**
* Constructor
* @param builder
*/
public StandaloneActorBuilder(ActorHandlerBuilder builder)
{
this(builder, null);
}
/**
* Constructor
*/
public StandaloneActorBuilder(ActorHandlerBuilder builder, ChannelReference from)
{
super(builder);
from(from);
actorBuilders.add(this);
}
@Override
public void build()
{
AbstractActor actor =
new StandaloneActor(handlerBuilder.build(),
this.mailbox,
this.exceptionHandler,
this.inputChannel,
this.outputChannel);
addActor(this.actorName, actor);
}
}
/**
* Implementation of {@link ChannelBuilder} for standalone engine
*/
private class StandaloneChannelBuilder extends AbstractChannelBuilder
{
/**
* {@inheritDoc}
*/
@Override
public final ChannelBuilder exposed()
{
throw new UnsupportedOperationException("asClass is supported only for Guice");
}
/**
* {@inheritDoc}
*/
@Override
public ChannelBuilder asClass(Class extends MessageChannel> channelClass)
{
try {
MessageChannel channel = channelClass.newInstance();
asInstance(channel);
} catch (InstantiationException e) {
//todo: change the exception handling
e.printStackTrace();
} catch (IllegalAccessException e) {
//todo: change the exception handling
e.printStackTrace();
}
return this;
}
@Override
public void build()
{
if (channelInstance == null) {
channelInstance = new DirectChannel();
}
if (channelInstance instanceof Nameable) {
((Nameable)channelInstance).setName(this.channelName);
}
AbstractSparrowEngine.this.channels.put(this.channelName, channelInstance);
if (channelInstance instanceof SparrowLifecycle) {
AbstractSparrowEngine.this.units.add((SparrowLifecycle) channelInstance);
}
}
}
//-------------------------------------------------------------------------------------------------------------------
// private methods
//-------------------------------------------------------------------------------------------------------------------
private void addActor(String actorName, AbstractActor actor)
{
actor.setName(actorName);
channels.put(actorName, actor);
this.units.add(actor);
}
//-------------------------------------------------------------------------------------------------------------------
// public methods
//-------------------------------------------------------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public MessageChannel getMessageChannel(ChannelReference channelRef)
{
String channelName = channelRef.name();
MessageChannel channel = channels.get(channelName);
return channel;
}
/**
* {@inheritDoc}
*/
@Override
public void start()
{
configure();
for (SparrowLifecycle unit : units) {
unit.onInit(this);
}
for (SparrowLifecycle unit : units) {
unit.onStart();
}
}
/**
* {@inheritDoc}
*/
@Override
public void shutdown() throws InterruptedException
{
for (SparrowLifecycle unit : units) {
unit.onStop();
}
}
/**
* Method does configuration of message system and build all his entities as channels, actors etc
*/
private void configure()
{
preBuilders = new LinkedList();
channelBuilders = new LinkedList();
actorBuilders = new LinkedList();
configureSparrow();
for (SparrowBuilder preBuilder : this.preBuilders) {
preBuilder.build();
}
for (ChannelBuilder channelBuilder : this.channelBuilders) {
channelBuilder.build();
}
for (SparrowBuilder actorBuilder : this.actorBuilders) {
actorBuilder.build();
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy