diff --git a/README.md b/README.md index 31d9306..6510948 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,86 @@ # ALMI (Abstraction Layer for Multiserver Infrastructure) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.matteobattilana/almi/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.matteobattilana/almi) [![CircleCI](https://circleci.com/gh/MatteoBattilana/ALMI.svg?style=svg)](https://circleci.com/gh/MatteoBattilana/ALMI) ## Description -ALMI is a JAVA multiserver framework for remote method calls management based on Netty and Jackson serialization, that adds an abstraction layer for multiserver communication. +ALMI is a Java multiserver framework for remote method calls management based on Netty and Jackson serialization, that adds an abstraction layer for multiserver communication. + +This framework will semplify the cluster management, making methods accessibile from other server, that has ALMI running on a node in the cluster. + +## Setup +Add ALMI dependencies in your pom.xml or eventually, go to the [Maven Repository](https://search.maven.org/artifact/com.matteobattilana/almi/0.1.0/jar) and download the jar library file. + +```xml + + ... + + com.matteobattilana + almi + 0.1.0 + + +``` + +## Simple usage + +The following code will instantiate an ALMI server that is listening on the 8888 port (if not set, it will use the one described in the [Default configuration](#Default configuration) section). + +```java +Almi server = AlmiBootstrap.bootstrap() + .withPort(8888) + .withRemoteCallTimeout(2000) + .withMethodsMapper(new MethodsMapper() + { + @Override + public void configure() + throws Exception + { + addMethods( + bindStatic(Calculator.class).method("execute", double.class, Calculator.Operation.class, double.class).withDefaultName(), + bind(new Calculator()).method(Calculator.class.getMethod("sqrt", double.class)).withName("positiveSqrt") + ); + } + }) + .start(); +``` + +### MethodsMapper +MethodsMapper will let you define the mapping beetwen the local method implementation and its remote name. This is used to make methods accessibile from other ALMI server, running on a node in the cluster. + +A MethodsMapper can be defined in-line as the one in previous example, or in a specific class. The `addMethods` will configure the methods passed as arguments, in order to expose them. +```java +public class MethodMapperImpl extends MethodsMapper +{ + private final ClientInformation mInstance1; + + public MethodMapperImpl( + ClientInformation instance1 + ) + { + mInstance1 = instance1; + } + + @Override + public void configure() + throws Exception + { + addMethods( + bind(mInstance1).method(ClientInformation.class.getMethod("helloWorld")).withDefaultName(), + bindStatic(ClientInformation.class).method(ClientInformation.class.getMethod("ping")).withDefaultName() + ); + } +} +``` + + +## Configuration +### Default configuration +The defaul configuration loaded from static constants from the Constants.java: + +```java +public static final int DEFAULT_PORT = 8888; +public static final int DEFAULT_CONNECTION_TIMEOUT = 10000; +public static final int DEFAULT_PROMISE_TIMEOUT = 10000; +public static final String DEFAULT_THREAD_NAME = "almi-service"; +public static final String DEFAULT_ADDRESS = "localhost"; +``` diff --git a/examples/calculator/AlmiCalculator.java b/examples/calculator/AlmiCalculator.java index 4b36570..a658714 100644 --- a/examples/calculator/AlmiCalculator.java +++ b/examples/calculator/AlmiCalculator.java @@ -2,7 +2,7 @@ import exceptions.InvisibleWrapperException; import socket.Almi; -import socket.bootstrap.DefaultAlmiBootstrap; +import socket.bootstrap.AlmiBootstrap; import socket.bootstrap.MethodsMapper; import java.util.Arrays; @@ -13,7 +13,7 @@ public class AlmiCalculator public static void main(String[] args) throws Exception { - Almi server = DefaultAlmiBootstrap.bootstrap() + Almi server = AlmiBootstrap.bootstrap() .withPort(8888) .withRemoteCallTimeout(2000) .withMethodsMapper(new MethodsMapper() diff --git a/examples/clientinformation/ClientInformation.java b/examples/clientinformation/ClientInformation.java index 7db05dc..4abcfa8 100644 --- a/examples/clientinformation/ClientInformation.java +++ b/examples/clientinformation/ClientInformation.java @@ -1,7 +1,7 @@ package clientinformation; import socket.Almi; -import socket.bootstrap.DefaultAlmiBootstrap; +import socket.bootstrap.AlmiBootstrap; import socket.bootstrap.MethodsMapper; import java.lang.management.ManagementFactory; @@ -11,7 +11,7 @@ public class ClientInformation implements Methods public static void main(String[] args) throws Exception { - Almi server = DefaultAlmiBootstrap.bootstrap() + Almi server = AlmiBootstrap.bootstrap() .withPort(8888) .withThreadName("ClientInformation-ALMI-server") .withRemoteCallTimeout(2000) diff --git a/examples/pingpong/AlmiPingPong.java b/examples/pingpong/AlmiPingPong.java new file mode 100644 index 0000000..21daf79 --- /dev/null +++ b/examples/pingpong/AlmiPingPong.java @@ -0,0 +1,33 @@ +package pingpong; + +import socket.Almi; +import socket.bootstrap.AlmiBootstrap; + +import java.util.Arrays; +import java.util.Collections; + +public class AlmiPingPong +{ + public static void main(String[] args) + throws Exception + { + Almi server = AlmiBootstrap.bootstrap() + .withPort(8888) + .withRemoteCallTimeout(2000) + .withMethodsMapper(new MethodsMapperImpl(new PingPong())) + .start(); + + PingPong.State state = PingPong.State.PING; + for(int i = 0; i < 10; i++) + { + state = server.callMethod( + "localhost", + 8888, + "ping", + Collections.singletonList(state) + ); + System.out.println(state); + } + server.stop(); + } +} diff --git a/examples/pingpong/MethodsMapperImpl.java b/examples/pingpong/MethodsMapperImpl.java new file mode 100644 index 0000000..75d147c --- /dev/null +++ b/examples/pingpong/MethodsMapperImpl.java @@ -0,0 +1,22 @@ +package pingpong; + +import socket.bootstrap.MethodsMapper; + +public class MethodsMapperImpl extends MethodsMapper +{ + private final PingPong mPingPong; + + public MethodsMapperImpl(PingPong pingPong) + { + mPingPong = pingPong; + } + + @Override + public void configure() + throws Exception + { + addMethods( + bind(mPingPong).method("getReponse", PingPong.State.class).withName("ping") + ); + } +} diff --git a/examples/pingpong/PingPong.java b/examples/pingpong/PingPong.java new file mode 100644 index 0000000..2fc95a5 --- /dev/null +++ b/examples/pingpong/PingPong.java @@ -0,0 +1,37 @@ +package pingpong; + +public class PingPong +{ + public enum State + { + PING("ping"), + PONG("pong"), + UNKNOWN(""); + + private final String mState; + + State(String state) + { + mState = state; + } + + @Override + public String toString() + { + return mState; + } + } + + public State getReponse(State request) + { + switch(request) + { + case PING: + return State.PONG; + case PONG: + return State.PING; + default: + return State.UNKNOWN; + } + } +} diff --git a/pom.xml b/pom.xml index 4d2749a..6fee0df 100644 --- a/pom.xml +++ b/pom.xml @@ -8,8 +8,8 @@ UTF-8 com.matteobattilana - com.matteobattilana - 1.0-SNAPSHOT + almi + 0.1.1 @@ -19,7 +19,6 @@ false - org.apache.maven.plugins maven-compiler-plugin @@ -29,8 +28,101 @@ 8 + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ossrh + https://oss.sonatype.org/ + true + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + jar + ALMI (Abstraction Layer for Multiserver Infrastructure) + ALMI is a Java multiserver framework for remote method calls management based on Netty and Jackson serialization, that adds an abstraction layer for multiserver communication. + https://github.com/MatteoBattilana/ALMI + + + The Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + + release + + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + scm:git:git://github.com/MatteoBattilana/ALMI.git + scm:git:ssh://github.com/MatteoBattilana/ALMI.git + http://github.com/MatteoBattilana/ALMI/tree/master + + + + + Matteo Battilana + matteo@matteobattilana.com + http://matteobattilana.com + http://matteobattilana.com + + diff --git a/src/main/java/socket/bootstrap/AlmiBootstrap.java b/src/main/java/socket/bootstrap/AlmiBootstrap.java index 0752a51..eedbe49 100644 --- a/src/main/java/socket/bootstrap/AlmiBootstrap.java +++ b/src/main/java/socket/bootstrap/AlmiBootstrap.java @@ -1,57 +1,144 @@ package socket.bootstrap; +import com.google.inject.Guice; +import com.google.inject.Injector; import exceptions.AlmiException; import exceptions.InvalidPropertiesFileException; +import exceptions.MethodMapperException; +import guice.AlmiModules; +import method.MethodDescriptor; import socket.Almi; +import socket.AlmiFactory; +import utils.Constants; +import utils.PropertiesUtils; import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; import java.util.Properties; -public interface AlmiBootstrap +public class AlmiBootstrap implements Bootstrap { - /** - * @param name - */ - AlmiBootstrap withThreadName(String name); - - /** - * @param address - */ - AlmiBootstrap withAddress(String address); - - /** - * @param port - */ - AlmiBootstrap withPort(int port); - - /** - * @param timeout - */ - AlmiBootstrap withConnectionTimeout(int timeout); - - /** - * @param timeout - */ - AlmiBootstrap withRemoteCallTimeout(int timeout); - - /** - * @param props - */ - AlmiBootstrap from(Properties props) - throws AlmiException; - - /** - * @param path - */ - AlmiBootstrap fromPropertiesFile(String path) - throws AlmiException; - - /** - * @param mapper - */ - AlmiBootstrap withMethodsMapper(MethodsMapper mapper) - throws AlmiException; - - Almi start() - throws AlmiException; + private final AlmiFactory mAlmiFactory; + + private Map mMethodDescriptorMap = new HashMap<>(); + private String mThreadName = Constants.DEFAULT_THREAD_NAME; + private String mSocketAddress = Constants.DEFAULT_ADDRESS; + private int mPort = Constants.DEFAULT_PORT; + private int mConnectTimeout = Constants.DEFAULT_CONNECTION_TIMEOUT; + private int mPromiseTimeout = Constants.DEFAULT_PROMISE_TIMEOUT; + + public static AlmiBootstrap bootstrap() + { + Injector injector = Guice.createInjector(new AlmiModules()); + + return new AlmiBootstrap( + injector.getInstance(AlmiFactory.class) + ); + } + + private AlmiBootstrap(AlmiFactory almiFactory) + { + mAlmiFactory = almiFactory; + } + + @Override + public Bootstrap from(Properties props) throws AlmiException + { + withThreadName(PropertiesUtils.optString(props, Constants.PROPERTY_THREAD_NAME, Constants.DEFAULT_THREAD_NAME)); + withAddress(PropertiesUtils.optString(props, Constants.PROPERTY_ADDRESS, Constants.DEFAULT_ADDRESS)); + withPort(PropertiesUtils.optInt(props, Constants.PROPERTY_PORT, Constants.DEFAULT_PORT)); + withConnectionTimeout(PropertiesUtils.optInt(props, Constants.PROPERTY_CONNECTION_TIMEOUT, Constants.DEFAULT_CONNECTION_TIMEOUT)); + withRemoteCallTimeout(PropertiesUtils.optInt(props, Constants.PROPERTY_PROMISE_TIMEOUT, Constants.DEFAULT_PROMISE_TIMEOUT)); + return this; + } + + @Override + public Bootstrap fromPropertiesFile(String path) + throws AlmiException + { + File propertiesFile = new File(path); + Properties props = new Properties(); + + try(InputStream is = new FileInputStream(propertiesFile)) + { + props.load(is); + return from(props); + } + catch(IOException e) + { + throw new InvalidPropertiesFileException(path, e); + } + } + + @Override + public Bootstrap withMethodsMapper(MethodsMapper mapper) throws AlmiException + { + try + { + mapper.configure(); + mMethodDescriptorMap = mapper.methodDescriptorMap(); + } + catch(Exception e) + { + throw new MethodMapperException(e); + } + return this; + } + + @Override + public Bootstrap withThreadName(String name) + { + mThreadName = name; + return this; + } + + @Override + public Bootstrap withAddress(String address) + { + mSocketAddress = address; + return this; + } + + @Override + public Bootstrap withPort(int port) + { + mPort = port; + return this; + } + + @Override + public Bootstrap withConnectionTimeout(int timeout) + { + mConnectTimeout = timeout; + return this; + } + + @Override + public Bootstrap withRemoteCallTimeout(int timeout) + { + mPromiseTimeout = timeout; + return this; + } + + @Override + public Almi start() + { + return build().start(); + } + + private Almi build() + { + return mAlmiFactory.create( + mMethodDescriptorMap, + mThreadName, + mSocketAddress, + mPort, + mConnectTimeout, + mPromiseTimeout + ); + } } diff --git a/src/main/java/socket/bootstrap/Bootstrap.java b/src/main/java/socket/bootstrap/Bootstrap.java new file mode 100644 index 0000000..a90cc50 --- /dev/null +++ b/src/main/java/socket/bootstrap/Bootstrap.java @@ -0,0 +1,55 @@ +package socket.bootstrap; + +import exceptions.AlmiException; +import socket.Almi; + +import java.util.Properties; + +public interface Bootstrap +{ + /** + * @param name + */ + Bootstrap withThreadName(String name); + + /** + * @param address + */ + Bootstrap withAddress(String address); + + /** + * @param port + */ + Bootstrap withPort(int port); + + /** + * @param timeout + */ + Bootstrap withConnectionTimeout(int timeout); + + /** + * @param timeout + */ + Bootstrap withRemoteCallTimeout(int timeout); + + /** + * @param props + */ + Bootstrap from(Properties props) + throws AlmiException; + + /** + * @param path + */ + Bootstrap fromPropertiesFile(String path) + throws AlmiException; + + /** + * @param mapper + */ + Bootstrap withMethodsMapper(MethodsMapper mapper) + throws AlmiException; + + Almi start() + throws AlmiException; +} diff --git a/src/main/java/socket/bootstrap/DefaultAlmiBootstrap.java b/src/main/java/socket/bootstrap/DefaultAlmiBootstrap.java deleted file mode 100644 index 055e7d6..0000000 --- a/src/main/java/socket/bootstrap/DefaultAlmiBootstrap.java +++ /dev/null @@ -1,144 +0,0 @@ -package socket.bootstrap; - -import com.google.inject.Guice; -import com.google.inject.Injector; -import exceptions.AlmiException; -import exceptions.InvalidPropertiesFileException; -import exceptions.MethodMapperException; -import guice.AlmiModules; -import method.MethodDescriptor; -import socket.Almi; -import socket.AlmiFactory; -import utils.Constants; -import utils.PropertiesUtils; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -public class DefaultAlmiBootstrap implements AlmiBootstrap -{ - private final AlmiFactory mAlmiFactory; - - private Map mMethodDescriptorMap = new HashMap<>(); - private String mThreadName = Constants.DEFAULT_THREAD_NAME; - private String mSocketAddress = Constants.DEFAULT_ADDRESS; - private int mPort = Constants.DEFAULT_PORT; - private int mConnectTimeout = Constants.DEFAULT_CONNECTION_TIMEOUT; - private int mPromiseTimeout = Constants.DEFAULT_PROMISE_TIMEOUT; - - public static DefaultAlmiBootstrap bootstrap() - { - Injector injector = Guice.createInjector(new AlmiModules()); - - return new DefaultAlmiBootstrap( - injector.getInstance(AlmiFactory.class) - ); - } - - private DefaultAlmiBootstrap(AlmiFactory almiFactory) - { - mAlmiFactory = almiFactory; - } - - @Override - public AlmiBootstrap from(Properties props) throws AlmiException - { - withThreadName(PropertiesUtils.optString(props, Constants.PROPERTY_THREAD_NAME, Constants.DEFAULT_THREAD_NAME)); - withAddress(PropertiesUtils.optString(props, Constants.PROPERTY_ADDRESS, Constants.DEFAULT_ADDRESS)); - withPort(PropertiesUtils.optInt(props, Constants.PROPERTY_PORT, Constants.DEFAULT_PORT)); - withConnectionTimeout(PropertiesUtils.optInt(props, Constants.PROPERTY_CONNECTION_TIMEOUT, Constants.DEFAULT_CONNECTION_TIMEOUT)); - withRemoteCallTimeout(PropertiesUtils.optInt(props, Constants.PROPERTY_PROMISE_TIMEOUT, Constants.DEFAULT_PROMISE_TIMEOUT)); - return this; - } - - @Override - public AlmiBootstrap fromPropertiesFile(String path) - throws AlmiException - { - File propertiesFile = new File(path); - Properties props = new Properties(); - - try(InputStream is = new FileInputStream(propertiesFile)) - { - props.load(is); - return from(props); - } - catch(IOException e) - { - throw new InvalidPropertiesFileException(path, e); - } - } - - @Override - public AlmiBootstrap withMethodsMapper(MethodsMapper mapper) throws AlmiException - { - try - { - mapper.configure(); - mMethodDescriptorMap = mapper.methodDescriptorMap(); - } - catch(Exception e) - { - throw new MethodMapperException(e); - } - return this; - } - - @Override - public AlmiBootstrap withThreadName(String name) - { - mThreadName = name; - return this; - } - - @Override - public AlmiBootstrap withAddress(String address) - { - mSocketAddress = address; - return this; - } - - @Override - public AlmiBootstrap withPort(int port) - { - mPort = port; - return this; - } - - @Override - public AlmiBootstrap withConnectionTimeout(int timeout) - { - mConnectTimeout = timeout; - return this; - } - - @Override - public AlmiBootstrap withRemoteCallTimeout(int timeout) - { - mPromiseTimeout = timeout; - return this; - } - - @Override - public Almi start() - { - return build().start(); - } - - private Almi build() - { - return mAlmiFactory.create( - mMethodDescriptorMap, - mThreadName, - mSocketAddress, - mPort, - mConnectTimeout, - mPromiseTimeout - ); - } -} diff --git a/src/main/java/socket/handler/MessageInboundHandler.java b/src/main/java/socket/handler/MessageInboundHandler.java index ab40ac0..722b08a 100644 --- a/src/main/java/socket/handler/MessageInboundHandler.java +++ b/src/main/java/socket/handler/MessageInboundHandler.java @@ -36,7 +36,6 @@ public MessageInboundHandler( public void channelActive(ChannelHandlerContext ctx) throws Exception { - System.out.println("Connected!"); super.channelActive(ctx); } @@ -45,7 +44,6 @@ protected void channelRead0(ChannelHandlerContext ctx, BaseMessage in) { try { - System.out.println("Received: " + in.getClass()); in.interpret(this, ctx); } catch(Exception e) @@ -61,7 +59,6 @@ protected void channelRead0(ChannelHandlerContext ctx, BaseMessage in) @Override public void channelInactive(ChannelHandlerContext ctx) { - System.out.println("Closed connection!"); ctx.fireChannelInactive(); } diff --git a/src/main/java/utils/Constants.java b/src/main/java/utils/Constants.java index 2c12af0..26725d3 100644 --- a/src/main/java/utils/Constants.java +++ b/src/main/java/utils/Constants.java @@ -5,7 +5,7 @@ public class Constants public static final int DEFAULT_PORT = 8888; public static final int DEFAULT_CONNECTION_TIMEOUT = 10000; public static final int DEFAULT_PROMISE_TIMEOUT = 10000; - public static final String DEFAULT_THREAD_NAME = "socket-service"; + public static final String DEFAULT_THREAD_NAME = "almi-service"; public static final String DEFAULT_ADDRESS = "localhost"; public static final String PROPERTY_THREAD_NAME = "thread_name"; diff --git a/src/test/java/it-test/ServerTest.java b/src/test/java/it-test/ServerTest.java index 7d04e1f..7b4d9b7 100644 --- a/src/test/java/it-test/ServerTest.java +++ b/src/test/java/it-test/ServerTest.java @@ -2,7 +2,7 @@ import org.junit.Before; import org.junit.Test; import socket.Almi; -import socket.bootstrap.DefaultAlmiBootstrap; +import socket.bootstrap.AlmiBootstrap; import socket.bootstrap.MethodsMapper; import java.util.Collections; @@ -18,12 +18,12 @@ public class ServerTest @Before public void setup() throws Exception { - mServer1 = DefaultAlmiBootstrap.bootstrap() + mServer1 = AlmiBootstrap.bootstrap() .withPort(mPort1) .withRemoteCallTimeout(2000) .start(); - mServer2 = DefaultAlmiBootstrap.bootstrap() + mServer2 = AlmiBootstrap.bootstrap() .withPort(mPort2) .withRemoteCallTimeout(2000) .withMethodsMapper(new MethodsMapper()