/* * Hibernate OGM, Domain model persistence for NoSQL datastores * * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.ogm.datastore.neo4j.remote.http.impl; import java.net.URI; import java.util.concurrent.TimeUnit; import javax.ws.rs.core.Response; import org.hibernate.ogm.datastore.neo4j.logging.impl.Log; import org.hibernate.ogm.datastore.neo4j.logging.impl.LoggerFactory; import org.hibernate.ogm.datastore.neo4j.remote.common.impl.RemoteNeo4jConfiguration; import org.hibernate.ogm.datastore.neo4j.remote.common.impl.RemoteNeo4jDatabaseIdentifier; import org.hibernate.ogm.datastore.neo4j.remote.http.json.impl.Statements; import org.hibernate.ogm.datastore.neo4j.remote.http.json.impl.StatementsResponse; import org.hibernate.ogm.datastore.neo4j.remote.http.request.impl.HttpNeo4jAuthenticationFacade; import org.hibernate.ogm.datastore.neo4j.remote.http.request.impl.HttpNeo4jTransactionFacade; import org.hibernate.ogm.datastore.neo4j.remote.http.request.impl.XStreamRequestHeaderFilter; import org.hibernate.ogm.datastore.neo4j.remote.http.transaction.impl.HttpNeo4jTransaction; import org.jboss.resteasy.client.jaxrs.BasicAuthentication; import org.jboss.resteasy.client.jaxrs.ResteasyClient; import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; /** * Access point to the remote Neo4j server. * * @author Davide D'Alto */ public class HttpNeo4jClient implements AutoCloseable { private static final int OK_STATUS_CODE = Response.Status.OK.getStatusCode(); private static final Log log = LoggerFactory.getLogger(); /** * Size of the client connection pool used by the RestEasy HTTP client */ private static final int CONNECTION_POOL_SIZE = 10; /** * Client for accessing the server */ private final ResteasyClient client; private final HttpNeo4jAuthenticationFacade authenticationClient; private final HttpNeo4jTransactionFacade neo4jFacade; private final RemoteNeo4jDatabaseIdentifier database; public HttpNeo4jClient(RemoteNeo4jDatabaseIdentifier database, RemoteNeo4jConfiguration configuration) { this.database = database; this.client = createRestClient( database, configuration ); this.authenticationClient = client.target( database.getServerUri() ).proxy( HttpNeo4jAuthenticationFacade.class ); this.neo4jFacade = client.target( database.getDatabaseUri() ).proxy( HttpNeo4jTransactionFacade.class ); } private static ResteasyClient createRestClient(RemoteNeo4jDatabaseIdentifier database, RemoteNeo4jConfiguration configuration) { ResteasyClientBuilder clientBuilder = new ResteasyClientBuilder(); if ( database.getUserName() != null ) { clientBuilder.register( new BasicAuthentication( database.getUserName(), database.getPassword() ) ); } if ( configuration.getConnectionCheckoutTimeout() != null ) { clientBuilder.connectionCheckoutTimeout( configuration.getConnectionCheckoutTimeout(), TimeUnit.MILLISECONDS ); } if ( configuration.getEstablishConnectionTimeout() != null ) { clientBuilder.establishConnectionTimeout( configuration.getEstablishConnectionTimeout(), TimeUnit.MILLISECONDS ); } if ( configuration.getConnectionTTL() != null ) { clientBuilder.connectionTTL( configuration.getConnectionTTL(), TimeUnit.MILLISECONDS ); } if ( configuration.getSocketTimeout() != null ) { clientBuilder.socketTimeout( configuration.getSocketTimeout(), TimeUnit.MILLISECONDS ); } clientBuilder.register( XStreamRequestHeaderFilter.INSTANCE ); // using a connection pool size > 1 causes a thread-safe pool implementation to be used under the hoods return clientBuilder.connectionPoolSize( CONNECTION_POOL_SIZE ).build(); } public void validateConnection() { Response response = authenticationClient.authenticate( database.getUserName() ); if ( response.getStatus() != OK_STATUS_CODE ) { throw log.connectionFailed( String.valueOf( database.getHost() ), String.valueOf( response.getStatus() ), response.getStatusInfo().getReasonPhrase() ); } } /** * Release all the resources */ @Override public void close() { client.close(); } public StatementsResponse executeQueriesInOpenTransaction(Long txId, Statements statements) { Response executeQuery = neo4jFacade.executeQuery( txId, statements ); try { return executeQuery.readEntity( StatementsResponse.class ); } finally { executeQuery.close(); } } public StatementsResponse executeQueriesInNewTransaction(Statements statements) { Response response = neo4jFacade.executeQuery( statements ); try { return response.readEntity( StatementsResponse.class ); } finally { response.close(); } } public HttpNeo4jTransaction beginTx() { Response response = neo4jFacade.beginTransaction(); try { Long txId = transactionId( response.getLocation() ); HttpNeo4jTransaction transaction = new HttpNeo4jTransaction( this, txId ); return transaction; } finally { response.close(); } } // The location should look something like: http://localhost:7474/db/data/transaction/{txId} private Long transactionId(URI location) { return Long.valueOf( location.getPath().substring( location.getPath().lastIndexOf( "/" ) + 1 ) ); } public void commit(Long txId) { Response response = neo4jFacade.commit( txId ); response.close(); } public void rollback(Long txId) { Response response = neo4jFacade.rollback( txId ); response.close(); } }