Skip to content

Commit

Permalink
Made a discovery built-in procedure, callable from the client over Bo…
Browse files Browse the repository at this point in the history
…lt only.
  • Loading branch information
jimwebber authored and Mark Needham committed May 18, 2016
1 parent 43238f4 commit d3f7dee
Show file tree
Hide file tree
Showing 42 changed files with 1,282 additions and 234 deletions.
Expand Up @@ -58,7 +58,6 @@
import org.neo4j.kernel.impl.core.StartupStatisticsProvider;
import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge;
import org.neo4j.kernel.impl.core.TokenNotFoundException;
import org.neo4j.kernel.impl.coreapi.CoreAPIAvailabilityGuard;
import org.neo4j.kernel.impl.logging.LogService;
import org.neo4j.kernel.impl.proc.ProcedureGDSFactory;
import org.neo4j.kernel.impl.proc.Procedures;
Expand Down Expand Up @@ -164,7 +163,7 @@ public DataSourceModule( final GraphDatabaseFacadeFactory.Dependencies dependenc

AtomicReference<QueryExecutionEngine> queryExecutor = new AtomicReference<>( QueryEngineProvider.noEngine() );
this.queryExecutor = queryExecutor::get;
Procedures procedures = setupProcedures( platformModule, editionModule.coreAPIAvailabilityGuard );
Procedures procedures = setupProcedures( platformModule, editionModule );

DbmsOperations dbmsOperations = new NonTransactionalDbmsOperations( procedures );
deps.satisfyDependency( dbmsOperations );
Expand Down Expand Up @@ -340,7 +339,7 @@ public Relationship newRelationshipProxy( long id, long startNodeId, int typeId,
};
}

private Procedures setupProcedures( PlatformModule platform, CoreAPIAvailabilityGuard coreAPIAvailabilityGuard )
private Procedures setupProcedures( PlatformModule platform, EditionModule editionModule )
{
File pluginDir = platform.config.get( GraphDatabaseSettings.plugin_dir );
Log internalLog = platform.logging.getInternalLog( Procedures.class );
Expand All @@ -360,7 +359,9 @@ private Procedures setupProcedures( PlatformModule platform, CoreAPIAvailability
procedures.registerComponent( Log.class, (ctx) -> proceduresLog );

// Register injected private API components: useful to have available in procedures to access the kernel etc.
ProcedureGDSFactory gdsFactory = new ProcedureGDSFactory( platform.config, platform.storeDir, platform.dependencies, storeId, this.queryExecutor, coreAPIAvailabilityGuard, platform.urlAccessRule );
ProcedureGDSFactory gdsFactory = new ProcedureGDSFactory( platform.config, platform.storeDir,
platform.dependencies, storeId, this.queryExecutor, editionModule.coreAPIAvailabilityGuard,
platform.urlAccessRule );
procedures.registerComponent( GraphDatabaseService.class, gdsFactory::apply );

// Below components are not public API, but are made available for internal
Expand All @@ -374,6 +375,8 @@ private Procedures setupProcedures( PlatformModule platform, CoreAPIAvailability
procedures.registerComponent( KernelTransaction.class, ( ctx ) -> ctx.get( KERNEL_TRANSACTION ) );
procedures.registerComponent( GraphDatabaseAPI.class, ( ctx ) -> platform.graphDatabaseFacade );

editionModule.registerProcedures(procedures);

return procedures;
}

Expand Down
Expand Up @@ -36,6 +36,7 @@
import org.neo4j.kernel.impl.factory.GraphDatabaseFacadeFactory.Configuration;
import org.neo4j.kernel.impl.locking.Locks;
import org.neo4j.kernel.impl.logging.LogService;
import org.neo4j.kernel.impl.proc.Procedures;
import org.neo4j.kernel.impl.store.format.RecordFormats;
import org.neo4j.kernel.impl.store.id.IdGeneratorFactory;
import org.neo4j.kernel.impl.transaction.TransactionHeaderInformationFactory;
Expand All @@ -52,6 +53,11 @@
*/
public abstract class EditionModule
{
public void registerProcedures( Procedures procedures )
{
// do nothing
}

public interface SPI
{
}
Expand Down
Expand Up @@ -27,11 +27,11 @@
import org.neo4j.graphdb.security.AuthorizationViolationException;
import org.neo4j.kernel.api.DataWriteOperations;
import org.neo4j.kernel.api.exceptions.ProcedureException;
import org.neo4j.kernel.api.security.AccessMode;
import org.neo4j.kernel.api.security.AccessMode.Static;
import org.neo4j.kernel.api.security.AuthSubject;

import static java.util.Collections.singletonList;
import static java.util.concurrent.TimeUnit.SECONDS;

import static junit.framework.TestCase.fail;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
Expand All @@ -40,6 +40,7 @@
import static org.hamcrest.core.IsEqual.equalTo;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import static org.neo4j.helpers.collection.Iterators.asList;
import static org.neo4j.kernel.api.proc.ProcedureSignature.procedureName;

Expand All @@ -59,7 +60,8 @@ public void listAllLabels() throws Throwable
commit();

// When
RawIterator<Object[],ProcedureException> stream = readOperationsInNewTransaction().procedureCallRead( procedureName( "db", "labels" ), new Object[0] );
RawIterator<Object[], ProcedureException> stream =
readOperationsInNewTransaction().procedureCallRead( procedureName( "db", "labels" ), new Object[0] );

// Then
assertThat( asList( stream ), contains( equalTo( new Object[]{"MyLabel"} ) ) );
Expand All @@ -71,11 +73,10 @@ public void failWhenCallingListAllLabelsInDbmsMode() throws Throwable
try
{
// When
RawIterator<Object[],ProcedureException> stream = dbmsOperations().procedureCallDbms( procedureName( "db", "labels" ), new Object[0],
AccessMode.Static.NONE );
dbmsOperations().procedureCallDbms( procedureName( "db", "labels" ), new Object[0], Static.NONE );
fail( "Should have failed." );
}
catch (Exception e)
catch ( Exception e )
{
// Then
assertThat( e.getClass(), equalTo( ProcedureException.class ) );
Expand All @@ -91,7 +92,8 @@ public void listPropertyKeys() throws Throwable
commit();

// When
RawIterator<Object[],ProcedureException> stream = readOperationsInNewTransaction().procedureCallRead( procedureName( "db", "propertyKeys" ), new Object[0] );
RawIterator<Object[], ProcedureException> stream = readOperationsInNewTransaction()
.procedureCallRead( procedureName( "db", "propertyKeys" ), new Object[0] );

// Then
assertThat( asList( stream ), contains( equalTo( new Object[]{"MyProp"} ) ) );
Expand All @@ -103,10 +105,10 @@ public void failWhenCallingListPropertyKeysInDbmsMode() throws Throwable
try
{
// When
RawIterator<Object[],ProcedureException> stream = dbmsOperations().procedureCallDbms( procedureName( "db", "propertyKeys" ), new Object[0], AccessMode.Static.NONE );
dbmsOperations().procedureCallDbms( procedureName( "db", "propertyKeys" ), new Object[0], Static.NONE );
fail( "Should have failed." );
}
catch (Exception e)
catch ( Exception e )
{
// Then
assertThat( e.getClass(), equalTo( ProcedureException.class ) );
Expand All @@ -123,7 +125,8 @@ public void listRelationshipTypes() throws Throwable
commit();

// When
RawIterator<Object[],ProcedureException> stream = readOperationsInNewTransaction().procedureCallRead( procedureName( "db", "relationshipTypes" ), new Object[0] );
RawIterator<Object[], ProcedureException> stream = readOperationsInNewTransaction()
.procedureCallRead( procedureName( "db", "relationshipTypes" ), new Object[0] );

// Then
assertThat( asList( stream ), contains( equalTo( new Object[]{"MyRelType"} ) ) );
Expand All @@ -135,10 +138,11 @@ public void failWhenCallingListRelationshipTypesInDbmsMode() throws Throwable
try
{
// When
RawIterator<Object[],ProcedureException> stream = dbmsOperations().procedureCallDbms( procedureName( "db", "relationshipTypes" ), new Object[0], AccessMode.Static.NONE );
dbmsOperations()
.procedureCallDbms( procedureName( "db", "relationshipTypes" ), new Object[0], Static.NONE );
fail( "Should have failed." );
}
catch (Exception e)
catch ( Exception e )
{
// Then
assertThat( e.getClass(), equalTo( ProcedureException.class ) );
Expand All @@ -149,21 +153,25 @@ public void failWhenCallingListRelationshipTypesInDbmsMode() throws Throwable
public void listProcedures() throws Throwable
{
// When
RawIterator<Object[],ProcedureException> stream =
readOperationsInNewTransaction().procedureCallRead( procedureName( "dbms", "procedures" ), new Object[0] );
RawIterator<Object[], ProcedureException> stream = readOperationsInNewTransaction()
.procedureCallRead( procedureName( "dbms", "procedures" ), new Object[0] );

// Then
assertThat( asList( stream ), containsInAnyOrder(
equalTo( new Object[]{"db.constraints", "db.constraints() :: (description :: STRING?)"} ),
equalTo( new Object[]{"db.indexes", "db.indexes() :: (description :: STRING?, state :: STRING?)"} ),
equalTo( new Object[]{"db.propertyKeys", "db.propertyKeys() :: (propertyKey :: STRING?)"}),
equalTo( new Object[]{"db.propertyKeys", "db.propertyKeys() :: (propertyKey :: STRING?)"} ),
equalTo( new Object[]{"db.labels", "db.labels() :: (label :: STRING?)"} ),
equalTo( new Object[]{"db.relationshipTypes", "db.relationshipTypes() :: (relationshipType :: STRING?)"}),
equalTo( new Object[]{"dbms.procedures", "dbms.procedures() :: (name :: STRING?, signature :: STRING?)"} ),
equalTo( new Object[]{"dbms.components", "dbms.components() :: (name :: STRING?, versions :: LIST? OF STRING?, edition :: STRING?)"} ),
equalTo( new Object[]{"dbms.changePassword", "dbms.changePassword(password :: STRING?) :: ()"}),
equalTo( new Object[]{"dbms.queryJmx", "dbms.queryJmx(query :: STRING?) :: (name :: STRING?, description :: STRING?, attributes :: MAP?)"})
));
equalTo( new Object[]{"db.relationshipTypes", "db.relationshipTypes() :: (relationshipType :: " +
"STRING?)"} ),
equalTo( new Object[]{"dbms.procedures", "dbms.procedures() :: (name :: STRING?, signature :: " +
"STRING?)"} ),
equalTo( new Object[]{"dbms.components", "dbms.components() :: (name :: STRING?, versions :: LIST? OF" +
" STRING?, edition :: STRING?)"} ),
equalTo( new Object[]{"dbms.changePassword", "dbms.changePassword(password :: STRING?) :: ()"} ),
equalTo( new Object[]{"dbms.queryJmx", "dbms.queryJmx(query :: STRING?) :: (name :: STRING?, " +
"description :: STRING?, attributes :: MAP?)"} )
) );
}

@Test
Expand All @@ -172,13 +180,10 @@ public void failWhenCallingListProceduresInDbmsMode() throws Throwable
try
{
// When
RawIterator<Object[],ProcedureException> stream =
dbmsOperations()
.procedureCallDbms( procedureName( "sys", "procedures" ), new Object[0],
AccessMode.Static.NONE );
dbmsOperations().procedureCallDbms( procedureName( "sys", "procedures" ), new Object[0], Static.NONE );
assertThat( "This should never get here", 1 == 2 );
}
catch (Exception e)
catch ( Exception e )
{
// Then
assertThat( e.getClass(), equalTo( ProcedureException.class ) );
Expand All @@ -194,7 +199,7 @@ public void callChangePasswordWithAccessModeInDbmsMode() throws Throwable
AuthSubject authSubject = mock( AuthSubject.class );

// When
RawIterator < Object[],ProcedureException> stream = dbmsOperations()
RawIterator<Object[], ProcedureException> stream = dbmsOperations()
.procedureCallDbms( procedureName( "dbms", "changePassword" ), inputArray, authSubject );

// Then
Expand All @@ -212,9 +217,7 @@ public void shouldFailWhenChangePasswordWithStaticAccessModeInDbmsMode() throws
inputArray[0] = "newPassword";

// When
RawIterator<Object[],ProcedureException> stream = dbmsOperations()
.procedureCallDbms( procedureName( "dbms", "changePassword" ), inputArray,
AccessMode.Static.NONE );
dbmsOperations().procedureCallDbms( procedureName( "dbms", "changePassword" ), inputArray, Static.NONE );
fail( "Should have failed." );
}
catch ( Exception e )
Expand All @@ -230,10 +233,11 @@ public void listAllComponents() throws Throwable
// Given a running database

// When
RawIterator<Object[],ProcedureException> stream =
readOperationsInNewTransaction().procedureCallRead( procedureName( "dbms", "components" ), new Object[0] );
RawIterator<Object[], ProcedureException> stream = readOperationsInNewTransaction()
.procedureCallRead( procedureName( "dbms", "components" ), new Object[0] );

// Then
assertThat( asList( stream ), contains( equalTo( new Object[]{"Neo4j Kernel", singletonList("dev"), "community"} ) ) );
assertThat( asList( stream ), contains( equalTo( new Object[]{"Neo4j Kernel", singletonList( "dev" ),
"community"} ) ) );
}
}
Expand Up @@ -22,43 +22,16 @@
import java.util.Collections;
import java.util.Set;

import org.neo4j.coreedge.server.AdvertisedSocketAddress;
import org.neo4j.coreedge.server.BoltAddress;
import org.neo4j.coreedge.server.CoreMember;

public interface ClusterTopology
{
ClusterTopology EMPTY = new ClusterTopology()
{
@Override
public AdvertisedSocketAddress firstTransactionServer()
{
throw new RuntimeException( "No core server found" );
}

@Override
public int getNumberOfCoreServers()
{
return 0;
}

@Override
public Set<CoreMember> getMembers()
{
return Collections.<CoreMember>emptySet();
}

@Override
public boolean bootstrappable()
{
return false;
}
};

AdvertisedSocketAddress firstTransactionServer();
boolean bootstrappable();

int getNumberOfCoreServers();
Set<CoreMember> coreMembers();

Set<CoreMember> getMembers();
Set<BoltAddress> edgeMembers();

boolean bootstrappable();
Set<BoltAddress> boltCoreMembers();
}
Expand Up @@ -19,7 +19,7 @@
*/
package org.neo4j.coreedge.discovery;

public interface CoreDiscoveryService extends EdgeDiscoveryService
public interface CoreTopologyService extends ReadOnlyTopologyService
{
void addMembershipListener( Listener listener );

Expand Down
Expand Up @@ -25,7 +25,7 @@

public interface DiscoveryServiceFactory
{
CoreDiscoveryService coreDiscoveryService( Config config );
CoreTopologyService coreDiscoveryService( Config config );

EdgeDiscoveryService edgeDiscoveryService( Config config, LogProvider logProvider );
EdgeTopologyService edgeDiscoveryService( Config config, LogProvider logProvider );
}
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2002-2016 "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.coreedge.discovery;

import org.neo4j.helpers.HostnamePort;

public interface EdgeTopologyService extends ReadOnlyTopologyService
{
void registerEdgeServer( HostnamePort address );
}

0 comments on commit d3f7dee

Please sign in to comment.