/**
* Copyright (c) 2002-2010 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.remote.inspect;
import java.io.PrintStream;
import org.neo4j.graphdb.Direction;
import org.neo4j.remote.RemoteConnection;
import org.neo4j.remote.RemoteResponse;
/**
* An adapter for simple implementation of {@link Inspector}.
* @author Tobias Ivarsson
*/
public abstract class InspectionAdapter implements Inspector
{
/**
* The types of the events in a {@link RemoteConnection}.
* @author Tobias Ivarsson
*/
public enum Event
{
/** Event received when a new remote connection is opened. */
OPEN,
/** Event received when a remote connection is closed. */
CLOSE,
/** Event received when a new transaction is started. */
BEGIN_TRANSACTION,
/** Event received when a new node is created. */
CREATE_NODE( "tx", "id" ),
/** Event received when a new relationship is created. */
CREATE_RELATIONSHIP( "tx", "start", "end", "type" ),
/** Event received when a relationship is fetched (by id). */
FETCH_RELATIONSHIP( "tx", "id" ),
/** Event received when properties for a node is requested. */
FETCH_NODE_PROPERTIES( "tx", "id" ),
/** Event received when properties for a relationship is requested. */
FETCH_RELATIONSHIP_PROPERTIES( "tx", "id" ),
/** Event received when relationships for a node is requested. */
FETCH_RELATIONSHIPS( "tx", "node", "direction", "types" ),
/** Event received when a transaction is committed. */
COMMIT( "tx" ),
/** Event received when a transaction is rolled back. */
ROLLBACK( "tx" ),
/** Event received when a node is deleted. */
DELETE_NODE( true, "id" ),
/** Event received when a relationship is deleted. */
DELETE_RELATIONSHIP( true, "id" ),
/** Event received when a property is set on a node. */
SET_NODE_PROPERTY( true, "id", "key", "value" ),
/** Event received when a property is set on a relationship. */
SET_RELATIONSHIP_PROPERTY( true, "id", "key", "value" );
private final String indentation;
private final String[] argNames;
private Event( String... argNames )
{
this( false, argNames );
}
private Event( boolean indent, String... argNames )
{
this.indentation = indent ? " " : "";
this.argNames = argNames;
}
private void print( PrintStream stream, String message )
{
stream.println( indentation + message );
}
private String call( Object[] args )
{
StringBuffer buffer = new StringBuffer( name() );
buffer.append( "(" );
appendArgs( buffer, args, argNames );
buffer.append( ")" );
return buffer.toString();
}
void traceFailure( PrintStream stream, Object[] args, Throwable ex )
{
print( stream, call( args ) + " failed with exception:" );
ex.printStackTrace( stream );
}
void traceSuccess( PrintStream stream, Object[] args )
{
print( stream, call( args ) + " sucessfull." );
}
void traceSuccess( PrintStream stream, Object[] args, Object result )
{
print( stream, call( args ) + " sucessfully returned " + result
+ "." );
}
}
/**
* Generic call. Called for any method that is not handled explicitly by a
* subclass.
* @param <T>
* the return type of the invoked method.
* @param call
* the marker identifying the event.
* @param type
* the return type of the invoked method.
* @param args
* the arguments to the method.
* @return an object that handles the status of the event.
*/
protected abstract <T> CallBack<T> call( Event call, Class<T> type,
Object... args );
/**
* Create a tracer that simply prints all events to a stream.
* @param stream
* The stream to print the events to.
* @return A tracing inspector.
*/
public static final Inspector trace( final PrintStream stream )
{
return new InspectionAdapter()
{
@Override
protected <T> CallBack<T> call( final Event call,
final Class<T> type, final Object... args )
{
if ( call == Event.COMMIT )
{
StringBuffer buffer = new StringBuffer( "PREPARE_COMMIT(" );
appendArgs( buffer, args, Event.COMMIT.argNames );
buffer.append( "):" );
stream.println( buffer.toString() );
}
return new CallBack<T>()
{
public void failure( Throwable ex )
{
call.traceFailure( stream, args, ex );
}
public void success( T result )
{
if ( call == Event.COMMIT )
{
stream.println( " sucessfull." );
}
else if ( type.equals( RemoteResponse.class )
|| type.equals( Void.class ) )
{
call.traceSuccess( stream, args );
}
else
{
call.traceSuccess( stream, args, result );
}
}
};
}
};
}
private static void appendArgs( StringBuffer buffer, Object[] args,
String[] argNames )
{
boolean addComma = false;
for ( int i = 0; i < args.length; i++ )
{
Object arg = args[ i ];
if ( addComma )
{
buffer.append( ", " );
}
if ( argNames != null && i < argNames.length )
{
buffer.append( argNames[ i ] );
buffer.append( "=" );
}
if ( arg.getClass().isArray() )
{
buffer.append( "[" );
appendArgs( buffer, ( Object[] ) arg, null );
buffer.append( "]" );
}
else if ( arg instanceof String )
{
String string = ( String ) arg;
buffer.append( '"' );
buffer.append( string );
buffer.append( '"' );
}
else
{
buffer.append( arg.toString() );
}
addComma = true;
}
}
public CallBack<Void> open()
{
return call( Event.OPEN, Void.class );
}
public CallBack<Void> close()
{
return call( Event.CLOSE, Void.class );
}
public CallBack<Integer> beginTransaction()
{
return call( Event.BEGIN_TRANSACTION, Integer.class );
}
public CallBack<RemoteResponse> createNode( int transactionId )
{
return call( Event.CREATE_NODE, RemoteResponse.class, transactionId );
}
public CallBack<RemoteResponse> createRelationship( int transactionId,
long startNodeId, long endNodeId, String relationshipTypeName )
{
return call( Event.CREATE_RELATIONSHIP, RemoteResponse.class,
transactionId, startNodeId, endNodeId, relationshipTypeName );
}
public CallBack<RemoteResponse> fetchRelationship( int transactionId,
long relationshipId )
{
return call( Event.FETCH_RELATIONSHIP, RemoteResponse.class,
transactionId, relationshipId );
}
public CallBack<RemoteResponse> fetchNodeProperties( int transactionId,
long nodeId )
{
return call( Event.FETCH_NODE_PROPERTIES, RemoteResponse.class,
transactionId, nodeId );
}
public CallBack<RemoteResponse> fetchRelationshipProperties(
int transactionId, long relationshipId )
{
return call( Event.FETCH_RELATIONSHIP_PROPERTIES, RemoteResponse.class,
transactionId, relationshipId );
}
public CallBack<RemoteResponse> fetchRelationships( int transactionId,
long rootNodeId, Direction direction, String[] typeNames )
{
return call( Event.FETCH_RELATIONSHIPS, RemoteResponse.class,
transactionId, rootNodeId, direction, typeNames );
}
public CallBack<Void> commit( int transactionId )
{
return call( Event.COMMIT, Void.class, transactionId );
}
public CallBack<Void> rollback( int transactionId )
{
return call( Event.ROLLBACK, Void.class, transactionId );
}
public CallBack<Void> setNodeProperty( long id, String key, Object value )
{
return call( Event.SET_NODE_PROPERTY, Void.class, id, key, value );
}
public CallBack<Void> setRelationshipProperty( long id, String key,
Object value )
{
return call( Event.SET_RELATIONSHIP_PROPERTY, Void.class, id, key,
value );
}
public CallBack<Void> deleteNode( long nodeId )
{
return call( Event.DELETE_NODE, Void.class, nodeId );
}
public CallBack<Void> deleteRelationship( long relationshipId )
{
return call( Event.DELETE_RELATIONSHIP, Void.class, relationshipId );
}
}