Neo4j Websockets Client/Server Framework - Server Library

This framework allows to connect clients with embedded Neo4j servers through websockets. Furthermore it offers high availability and load balancing to the clients when using Neo4j in a cluster.

License

License

Categories

Categories

Neo4J Data Databases
GroupId

GroupId

de.oliverwetterau.neo4j
ArtifactId

ArtifactId

websockets-server
Last Version

Last Version

0.9.0
Release Date

Release Date

Type

Type

jar
Description

Description

Neo4j Websockets Client/Server Framework - Server Library
This framework allows to connect clients with embedded Neo4j servers through websockets. Furthermore it offers high availability and load balancing to the clients when using Neo4j in a cluster.
Project URL

Project URL

https://github.com/owetterau/neo4j-websockets
Source Code Management

Source Code Management

https://github.com/owetterau/neo4j-websockets.git

Download websockets-server

How to add to project

<!-- https://jarcasting.com/artifacts/de.oliverwetterau.neo4j/websockets-server/ -->
<dependency>
    <groupId>de.oliverwetterau.neo4j</groupId>
    <artifactId>websockets-server</artifactId>
    <version>0.9.0</version>
</dependency>
// https://jarcasting.com/artifacts/de.oliverwetterau.neo4j/websockets-server/
implementation 'de.oliverwetterau.neo4j:websockets-server:0.9.0'
// https://jarcasting.com/artifacts/de.oliverwetterau.neo4j/websockets-server/
implementation ("de.oliverwetterau.neo4j:websockets-server:0.9.0")
'de.oliverwetterau.neo4j:websockets-server:jar:0.9.0'
<dependency org="de.oliverwetterau.neo4j" name="websockets-server" rev="0.9.0">
  <artifact name="websockets-server" type="jar" />
</dependency>
@Grapes(
@Grab(group='de.oliverwetterau.neo4j', module='websockets-server', version='0.9.0')
)
libraryDependencies += "de.oliverwetterau.neo4j" % "websockets-server" % "0.9.0"
[de.oliverwetterau.neo4j/websockets-server "0.9.0"]

Dependencies

compile (4)

Group / Artifact Type Version
de.oliverwetterau.neo4j : websockets-core jar 0.9.0
org.springframework : spring-websocket jar 4.1.7.RELEASE
org.aspectj : aspectjweaver jar 1.8.7
org.aspectj : aspectjtools jar 1.8.7

provided (2)

Group / Artifact Type Version
org.neo4j : neo4j-enterprise jar 2.2.5
org.neo4j.app : neo4j-server-enterprise jar 2.2.5

Project Modules

There are no modules declared in this project.

Neo4j Websockets Client/Server Framework

This framework allows to connect clients with Neo4j servers through websockets. Furthermore it offers high availability and load balancing to the clients when using Neo4j in a cluster.

Spring Framework is used for dependency injection.

Jar files for the framework are available from Maven Central Repository: maven.org - neo4j-websockets

An example project showing the use of client and server part of the framework can be found here: github - neo4j-websockets-example

Server

Configuration and Startup

This framework is a plugin for the server side. Hence, when using the provided example pom for maven, a fat Jar will be created that can be copied into the plugins directory of a Neo4j (2.3) server (community or enterprise).

An example-pom.xml that will create such a Jar can be found in the doc directory.

To be able to pickup the functionality added by yourself, you must let the framework know which packages need to be scanned for Spring beans. To do this, simply add a line like the following to conf/neo4j.properties

# replace com.example.project with your root package name
websocket_packages=com.example.project

There are three more settings in conf/neo4j.properties that you can add and change:

# hostname and port the websockets server shall listen to
# defaults to 0.0.0.0:8765 if not set
websocket_host=192.168.1.10:9999

# path for management connections
# defaults to /ws/management if not set
websocket_management_path=/websockets/management-connection

# path for data connections
# defaults to /ws/data if not set
websocket_data_path=/websockets/data-connection

Websockets Message Handling

All incoming data message will be passed to controllers based on controller and method name. To make controllers and methods known to the framework, annotations are being used.

Annotations

There are two annotations: @MessageController("name") and @MessageMethod.

@MessageController("abc") defines a class to be the handler of all messages marked with the service name "abc".

@MessageMethod defines a method to deal with messages marked with the method's name. The signature of methods annotated with @MessageMethod must always be:

public Result methodName(JsonNode jsonNode)

For example:

@MessageController("system")
public class SystemController {
    private final SchemaService schemaService;

    @Autowired
    public SystemController(SchemaService schemaService) {
        this.schemaService = schemaService;
    }

    @MessageMethod
    public Result<Boolean> initWithFakeData(JsonNode jsonNode) {
        try {
            schemaService.initWithFakeData();
        }
        catch (Exception e) {
            return new Result<>(e);
        }

        return new Result<>(true);
    }
}

All data to be used by these methods is being passed by a JsonNode.

Result, Error, JsonObjectMapper and ThreadLocale

These classes are tied closely together. Result is the expected format on the client side when receiving answers to a websocket message. It can contain any data (Result is generic) or errors. Hence, Error is the expected format of error messages on the client side.

The Jackson library is used for serialization and deserialization of the messages sent between client and server using binary json (SMILE). To allow for the addition of custom serializers, JsonObjectMapper has to be initialized with an instance of JsonObjectSerializers (or with null). Result and Error will use JsonObjectMapper to serialize themselves. Furthermore, all incoming messages on the server side will be deserialzed with JsonObjectMapper.

As each message coming from the client also contains a Locale defining the language that shall be used an instance of ThreadLocale is being used by JsonObjectMapper for serialization with the correct language settings. A class implementing ThreadLocale therefore must be created and made available as a @Service. Here is an example implementation:

@Service
public class LanguageSettings implements ThreadLocale {
    protected static ThreadLocal<ISOLanguage> isoLanguageThreadLocal = new ThreadLocal<>();

    public static void setLanguage(final ISOLanguage isoLanguage) {
        isoLanguageThreadLocal.set(isoLanguage);
    }

    public static ISOLanguage getLanguage() {
        ISOLanguage isoLanguage = isoLanguageThreadLocal.get();
        return (isoLanguage == null) ? ISOLanguage.EN_US : isoLanguage;
    }

    @Override
    public void setLocale(final Locale locale) {
        setLanguage(ISOLanguage.getByLocale(locale));
    }

    @Override
    public Locale getLocale() {
        return getLanguage().getLocale();
    }
}

Client

Configuration and Startup

Application Settings

You can configure the websocket connections to the server using the ApplicationSettings path.

Servers

The minimum information required to use the framework is the list of servers that the framework shall connect to. Setting this information is as easy as follows:

String[] uris = new String[] { "ws://192.168.1.10:8765", "ws://192.168.1.11:8765", "ws://192.168.1.12:8765"};
ApplicationSettings.setServerURIs(uris);
Paths (optional)

The paths used on the server must be same for all servers as it is not possible to configure paths per server (yet).

Obviously, paths defined on the client must be in sync with paths defined on the servers. Setting them is easy:

ApplicationSettings.setManagementPath("/websockets/management-connection");
ApplicationSettings.setManagementPath("/websockets/data-connection");

It is not necessary to set these paths. Default values "/ws/management" and "/ws/data" will be used, if you do not manually set them.

Protocol (optional)

As a default a binary protocol (Jackson Smile) will be used for the data connection between client and server. If you want to change that to a clear text communication, you can do it like this:

ApplicationSettings.setBinaryCommunication(false);

Startup

Similar to the server, it is important add this framework's package to the list of packages being scanned by Spring to activate it's functionality:

@SpringBootApplication
@ComponentScan(basePackages = {"com.example", "de.oliverwetterau.neo4j.websockets"})
public class Application {
    public static void main(String[] arguments) throws Exception {
        ApplicationSettings.setServerURIs(System.getProperty("database.server.uris").split(","));
        SpringApplication.run(Application.class, arguments);
    }
}

That's all to connect the client to the servers...

Sending and Retrieving

DatabaseService basically offers two different methods of communication with the servers: getData and writeDataWithResult whereas the first is being used for pure read access and the latter should be used for all messages that may lead to writes to Neo4j.

These method have four different signatures:

Result<JsonNode> getData(String service, String method)
Result<JsonNode> getData(String service, String method, JsonNode parameters)
Result<JsonNode> getData(String service, String method, Locale locale)
Result<JsonNode> getData(String service, String method, JsonNode parameters, Locale locale)
Result<JsonNode> writeDataWithResult(String service, String method)
Result<JsonNode> writeDataWithResult(String service, String method, JsonNode parameters)
Result<JsonNode> writeDataWithResult(String service, String method, Locale locale)
Result<JsonNode> writeDataWithResult(String service, String method, JsonNode parameters, Locale locale)

These methods are being used as follows:

  • service is the name of the service on the server which must be annotated with @MessageController("service").
  • method is the name of the method of the corresponding service which must be annotated with @MessageMethod.
  • parameters is a json node that can be used to pass any data to the called method.
  • locale is the locale that shall be used on the server side when serializing the answer.

How to Use it

You can find a description of how to use this framework and an example project here:

How to - Server

How to - Client

Example Project

Example Project

An example project using this framework can be found here:

https://github.com/owetterau/neo4j-websockets-example

Some Final Thoughts

I know that this description is not the most detailed one, yet. As work on this framework continues and maybe a few remarks come in I hopefully will be able to make it better...

Versions

Version
0.9.0