de.oliverwetterau.neo4j.websockets.client.Database Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of websockets-client Show documentation
Show all versions of websockets-client Show documentation
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.
The newest version!
package de.oliverwetterau.neo4j.websockets.client;
import de.oliverwetterau.neo4j.websockets.client.helpers.ConcurrentSequence;
import de.oliverwetterau.neo4j.websockets.core.data.Error;
import de.oliverwetterau.neo4j.websockets.core.data.Result;
import de.oliverwetterau.neo4j.websockets.core.data.json.JsonObjectMapper;
import de.oliverwetterau.neo4j.websockets.core.i18n.ThreadLocale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
/**
* Manages connections to a cluster of Neo4j servers, deals with load balances and provides functionality to send read
* and write messages to Neo4j server using web sockets.
*
* @author Oliver Wetterau
* @version 2015-09-01
*/
@Service
public class Database implements ClusterListener {
private static final Logger logger = LoggerFactory.getLogger(Database.class);
/** list all servers in the managed cluster */
protected final Set SERVERS = new ConcurrentSkipListSet<>();
/** multi threaded sequence used for load balancing of read servers */
protected final ConcurrentSequence readSequence = new ConcurrentSequence();
/** list of all servers currently acting as slaves / read servers */
protected Server[] readServers;
/** currently active master / write server */
protected Server writeServer = null;
/** Locale used in current thread */
protected final ThreadLocale threadLocale;
/**
* Constructor
* @param serverUri class which returns a list of Neo4j server URIs
* @param jsonObjectMapper json serialization
* @param threadLocale language settings
*/
@Autowired
public Database(final ServerUri serverUri, final JsonObjectMapper jsonObjectMapper, final ThreadLocale threadLocale) {
String[] uris = serverUri.getServerUris();
Server server;
Server masterServer = null;
// initially set JsonObjectMapper in all classes that need it
Server.setJsonObjectMapper(jsonObjectMapper);
WebSocketHandler.setJsonObjectMapper(jsonObjectMapper);
Result.setJsonObjectMapper(jsonObjectMapper);
Error.setJsonObjectMapper(jsonObjectMapper);
this.threadLocale = threadLocale;
// fill list of cluster servers
for (String uri : uris) {
server = new Server(this, uri, threadLocale);
try {
server.connect();
}
catch (Exception e) {
logger.error("[constructor] server '{}' is not available", uri);
}
SERVERS.add(server);
if (server.isMaster()) {
masterServer = server;
}
}
// remember currently active master / write server
setWriteServer(masterServer);
}
/**
* Returns the server identified by Neo4j cluster id.
* @param id Neo4j cluster id
* @return server identified by id
*/
public Server getServerById(final String id) {
for (Server server : SERVERS) {
if (server.getId().equals(id)) {
return server;
}
}
return null;
}
/**
* Returns the server identified by Neo4j cluster id.
* @param uri Neo4j server uri
* @return server identified by id
*/
public Server getServerByUri(final String uri) {
for (Server server : SERVERS) {
if (server.getManagementUri().equals(uri)) {
return server;
}
}
return null;
}
/**
* Adds a server to the list of available servers.
* @param id Neo4j cluster id
*/
public synchronized void onServerAvailable(final String id, final String role) {
logger.debug("[onServerAvailable] id = {}, role = {}", id, role);
Server server = getServerById(id);
boolean isMaster = role.equals("master");
boolean isSlave = role.equals("slave");
if (server == null) {
return;
}
if (server.isAvailable()) {
if (server.isMaster() && isMaster) return;
if (!server.isMaster() && isSlave) return;
}
if (isMaster || isSlave) {
server.setAvailable(true);
try {
server.connect();
}
catch (Exception e) {
logger.error("[onServerAvailable]", e);
}
}
server.setAvailable(true);
if (isMaster) {
setWriteServer(server);
}
refreshServers();
}
/**
* Removes a server from the list of available servers.
* @param id Neo4j cluster id
*/
public synchronized void onServerUnavailable(final String id) {
logger.debug("[onServerUnavailable] id = {}", id);
Server server = getServerById(id);
if (server != null) {
server.setAvailable(false);
refreshServers();
}
}
/**
* Add a server to the list of available servers.
* @param id Neo4j cluster id
* @param uri websocket uri
*/
public synchronized void onServerReconnected(final String id, final String uri) {
logger.debug("[onServerReconnected] id = {}, uri = {}", id, uri);
if (id.length() == 0) {
Server server = getServerByUri(uri);
server.register();
server.setAvailable(true);
}
refreshServers();
}
/**
* Changes the current master / write server.
* @param writeServer Neo4j server to use for write access
*/
public synchronized void setWriteServer(final Server writeServer) {
logger.debug("[setWriteServer] uri = {}", (writeServer == null) ? "NULL" : writeServer.getDataUri());
if (this.writeServer != null && writeServer != null && this.writeServer.equals(writeServer)) return;
this.writeServer = writeServer;
refreshServers();
}
/**
* Reorganises the list of read servers based on current master / write server and list of available servers.
*/
protected synchronized void refreshServers() {
StringBuilder refreshResult = new StringBuilder();
Set availableReadServers = new HashSet<>();
Server[] newReadServers;
if (writeServer == null || !writeServer.isAvailable()) {
logger.debug("[refreshServers] write server is null or not available");
for (Server server : SERVERS) {
if (server.isAvailable() && server.isMaster()) {
writeServer = server;
logger.debug("[refreshServers] new writeServer: id = {}, uri = {}", writeServer.getId(), writeServer.getManagementUri());
}
}
if (writeServer == null) {
readServers = new Server[0];
return;
}
}
for (Server server : SERVERS) {
logger.debug("[refreshServers] server: id = {}, isAvailable = {}", server.getId(), server.isAvailable());
if (!server.getId().equals(writeServer.getId()) && server.isAvailable()) {
logger.debug("[refreshServers] add read server");
availableReadServers.add(server);
}
}
if (availableReadServers.size() == 0) {
logger.debug("[refreshServers] available read servers = 0");
newReadServers = new Server[1];
newReadServers[0] = writeServer;
refreshResult
.append("refreshServers: read servers = ")
.append(writeServer.getId())
.append("=>")
.append(writeServer.getManagementUri())
.append(", write servers = ")
.append(writeServer.getId())
.append("=>")
.append(writeServer.getManagementUri());
}
else {
logger.debug("[refreshServers] available read servers = {}", availableReadServers.size());
newReadServers = new Server[availableReadServers.size()];
int i = 0;
refreshResult.append("refreshServers: read servers = [");
for (Server server : availableReadServers) {
newReadServers[i++] = server;
refreshResult
.append(server.getId())
.append("=>")
.append(server.getManagementUri())
.append(" ");
}
refreshResult
.append(", write servers = ")
.append(writeServer.getId())
.append("=>")
.append(writeServer.getManagementUri())
.append("]");
}
readServers = newReadServers;
logger.debug("[refreshServers] {}", refreshResult.toString());
}
/**
* Gets an read server from the list of available servers using round robing load balancing.
* @return server for read access
*/
protected Server getReadServer() {
Server[] servers = readServers;
return servers[readSequence.incrementAndGet(servers.length)];
}
/**
* Gets the currently active master / write server.
* @return server for write access
*/
protected Server getWriteServer() {
return writeServer;
}
/**
* Sends a message (which will probably create a write access) to the Neo4j cluster.
* @param message binary json message (usually json format)
* @throws ConnectionNotAvailableException no connection to server exception
*/
public void sendWriteMessage(final byte[] message) throws ConnectionNotAvailableException {
sendWriteMessage(message, getWriteServer());
}
/**
* Sends a message (which will probably create a write access) to the Neo4j cluster.
* @param message binary json message (usually json format)
* @param server server that shall be used to send the message
* @throws ConnectionNotAvailableException no connection to server exception
*/
public void sendWriteMessage(final byte[] message, final Server server) throws ConnectionNotAvailableException {
DataConnection connection = server.getConnection();
if (connection == null) {
throw new ConnectionNotAvailableException(server);
}
connection.send(message);
server.returnConnection(connection);
}
/**
* Sends a message (which will probably create a write access) to the Neo4j cluster and waits for a reply.
* @param message binary json message (usually json format)
* @return result text message (usually json format)
* @throws ConnectionNotAvailableException no connection to server exception
*/
public byte[] sendWriteMessageWithResult(byte[] message) throws ConnectionNotAvailableException {
return sendWriteMessageWithResult(message, getWriteServer());
}
/**
* Sends a message (which will probably create a write access) to the Neo4j cluster and waits for a reply.
* @param message binary json message (usually json format)
* @param server server that shall be used to send the message
* @return result text message (usually json format)
* @throws ConnectionNotAvailableException no connection to server exception
*/
public byte[] sendWriteMessageWithResult(final byte[] message, final Server server) throws ConnectionNotAvailableException {
DataConnection connection = server.getConnection();
if (connection == null) {
throw new ConnectionNotAvailableException(server);
}
byte[] result = connection.sendWithResult(message);
server.returnConnection(connection);
return result;
}
/**
* Sends a message (only read access) to the Neo4j cluster and waits for a reply.
* @param message binary json message
* @return result binary json message
* @throws ConnectionNotAvailableException no connection to server exception
*/
public byte[] sendReadMessage(final byte[] message) throws ConnectionNotAvailableException {
return sendReadMessage(message, getReadServer());
}
/**
* Sends a message (only read access) to the Neo4j cluster and waits for a reply.
* @param message binary json message
* @param server server that shall be used to send the message
* @return result binary json message
* @throws ConnectionNotAvailableException no connection to server exception
*/
public byte[] sendReadMessage(final byte[] message, final Server server) throws ConnectionNotAvailableException{
DataConnection connection = server.getConnection();
if (connection == null) {
throw new ConnectionNotAvailableException(server);
}
byte[] result = connection.sendWithResult(message);
server.returnConnection(connection);
return result;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy