I am trying to find the neo4j cluster health using java API. I see CLI CALL dbms.cluster.overview() is there any equivalent java api for this
1. Variant "Spring Boot"
If Spring Boot with Spring Data Neo4J is an option for you, you could define a DAO which executes your cypher statement and receives the result in an own QueryResult class.
1.1 GeneralQueriesDAO
#Repository
public interface GeneralQueriesDAO extends Neo4jRepository<String, Long> {
#Query("CALL dbms.cluster.overview() YIELD id, addresses, role, groups, database;")
ClusterOverviewResult[] getClusterOverview();
}
1.2 ClusterOverviewResult
#QueryResult
public class ClusterOverviewResult {
private String id; // This is id of the instance.
private List<String> addresses; // This is a list of all the addresses for the instance.
private String role; // This is the role of the instance, which can be LEADER, FOLLOWER, or READ_REPLICA.
private List<String> groups; // This is a list of all the server groups which an instance is part of.
private String database; // This is the name of the database which the instance is hosting.
// omitted default constructor as well getter and setter for clarity
}
1.3 Program flow
#Autowired
private GeneralQueriesDAO generalQueriesDAO;
[...]
ClusterOverviewResult[] clusterOverviewResult = generalQueriesDAO.getClusterOverview();
2. Variant "Without Spring"
Without Spring Boot the rough procedure could be:
Session session = driver.session();
StatementResult result = session.run("Cypher statement");
3. Variant "HTTP endpoints"
Another option could be to use of the HTTP endpoints for monitoring the health of a Neo4j Causal Cluster.
Related
My application is deployed is deployed in azure cloud with Azure service bus configuration.
when i track log i can see many log related to connection aborted in info, warning and in error also.
com.azure.core.amqp.exception.AmqpException: org.apache.qpid.proton.engine.TransportException: connection aborted
Try to track this error, but not find any specific solution, why this log coming.
Here using the following way, I wasn't getting any exceptions while using azure service bus with spring boot JMS
Now I am configuring the service bus in the application.properties file as below :
spring.jms.servicebus.connection-string=Endpoint=<Connection String>
spring.jms.servicebus.pricing-tier=<Price Tier>
Now I have simple Rest api which just sends a message to the Azure service Bus
#RestController
public class PostController {
private static final String DESTINATION_NAME = "<queueName>";
#Autowired
private JmsTemplate jmsTemplate;
#PostMapping("/messages")
public String postMessage(#RequestParam String message) {
jmsTemplate.convertAndSend(DESTINATION_NAME, new Test(message));
return message;
}
}
Here Test is just a class with variable name and we are sending the object of class Test
output:
I'm using Spring Boot 2.1. I'm also using the H2 in memory database. I have created this file at src/test/resources/data.sql:
insert into roles (id, name) values ('1be4965cb4f311eaa2b76a0000c30600', 'USER');
insert into roles (id, name) values ('1be6b2acb4f311eaa2b76a0000c30600', 'ADMIN');
insert into privileges (id, name) values ('1be4965cb4f311eaa2b76a0000c30600', 'USER');
insert into privileges (id, name) values ('1be6b2acb4f311eaa2b76a0000c30600', 'SUPER_ADMIN');
insert into roles_privileges (role_id, privilege_id) VALUES ('1be4965cb4f311eaa2b76a0000c30600', '1be4965cb4f311eaa2b76a0000c30600');
insert into roles_privileges (role_id, privilege_id) VALUES ('1be6b2acb4f311eaa2b76a0000c30600', '1be6b2acb4f311eaa2b76a0000c30600');
insert into occasions (id, name) values ('97c625b8b63511ea9d386a0000c30600', 'Birthday');
insert into users (id, email, enabled, first_name, last_name, password, token_expired) values ('aa7625b8b63512929d386a0000c30600', 'me#example.com', true, 'lone', 'ranger', 'password', false);
insert into users_roles (user_id, role_id) values ('aa7625b8b63512929d386a0000c30600', '1be6b2acb4f311eaa2b76a0000c30600');
I would like to create a spring boot integration test to test the following method ...
#RestController
#RequestMapping("/api/cards")
public class CardController {
#Autowired
private CardService cardService;
#PostMapping
#ResponseStatus(code = HttpStatus.CREATED)
public void create(#RequestBody Card card, #AuthenticationPrincipal User loggedInUser) {
card.setAuthor(loggedInUser);
cardService.save(card);
}
and I would like to load the one user in my database, but I'm not quite sure the easiest way to do that. I tried "#WithMockUser" but that isn't loading this user ...
#SpringBootTest(classes = CardmaniaApplication.class,
webEnvironment = WebEnvironment.RANDOM_PORT)
public class CardControllerIntegrationTest {
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private ICardRepository cardRepository;
#Autowired
private IUserRepository userRepository;
#Autowired
private IOccasionRepository occasionRepository;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Value("${jwt.http.request.header}")
private String tokenHeader;
#BeforeEach
void setup() {
UserDetails user = (UserDetails) userRepository.findAll().get(0);
final String token = jwtTokenUtil.generateToken(user);
restTemplate.getRestTemplate().setInterceptors(
Collections.singletonList((request, body, execution) -> {
request.getHeaders()
.add(this.tokenHeader, "Bearer " + token);
return execution.execute(request, body);
}));
}
#Test
#WithMockUser(authorities = {"ADMIN"})
void createCard() throws Exception {
final Card card = new Card();
final User author = userRepository.findAll().get(0);
card.setAuthor(author);
final Occasion occasion = occasionRepository.findAll().get(0);
card.setOccasion(occasion);
byte[] image = new byte[] {1, 1, 1, 1};
card.setImage(image);
ResponseEntity<String> responseEntity = this.restTemplate
.postForEntity("http://localhost:" + port + "/api/cards", card, String.class);
assertEquals(201, responseEntity.getStatusCodeValue());
List<Card> cards = cardRepository.findByOccasion(occasion);
assertThat(cards.size()).isEqualTo(1);
final Card cardEntity = cards.get(0);
assertThat(cardEntity.getImage()).isEqualTo(image);
}
}
I'm fairly new to spring boot and so unfamiliar with the simplest way to pre-load a user into my security principal.
I discovered the answer is to use the "#WithUserDetails" annotation
#Test
#WithUserDetails("me#example.com")
void registrationWorksThroughAllLayers() throws Exception {
This will use your autowired class that implements UserDetailsService and invoke its loadUserByUsername with the annotated value, "me#example.com," in the code above.
This setup is currently conflicting. One the one side you provide a mocked user for the SecurityContext using #WithMockUser and on the other side, you prepare a valid JWT token for actual authentication.
#WithMockUser is usually used in tests to e.g. easily access protected endpoints without creating a request with correct authentication information (like JWT or basic auth).
So you should pick one of the current approaches: Either go for mocking the user OR generate the JWT and access your endpoint.
The reason your #WithMockUser is not working currently, might be related to the default username Spring Security picks. If you want this to match your user in your database, consider configuring it: #WithMockUser(username="YOUR_USERNAME",roles={"USER","ADMIN"})
To debug your application further, you can temporarily add
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
to the endpoint you are testing and debug it to understand which user is now actually part of the SecurityContext.
We are generating 48 hour projection data using 6 months historical network traffic data. The final projection data is loaded into Neo4J to support a front end web application where users can search a server and see its future communication/data transfers with other servers within the organization. We defined a Host node and relations 1 to 48 hrs (1H, 2H, 3H....48H). The following regular cypher query works fine and returns data with relation information.
MATCH p=(parent:HOST)-[r]->(child:HOST)
WHERE parent.HOST_NAME = " "
RETURN p, type(r)
But, the SDN (Spring Data Neo4j) setup is not returning relation info. The node entity is defined as follows.
#NodeEntity
public class Host {
#GraphId
private Long id;
private String Host_Name;
#Relationship(type="")
private List<Host> hosts;
public List<Host> getHosts() {
return hosts;
}
public Long getId() {
return id;
}
public String getName() {
return Host_Name;
}
}
And repository defined as
public interface HostRepository extends Neo4jRepository<Host, Long> {
#Query("MATCH p=(parent:Host)<-[r:`28H`]-(child:Host) WHERE parent.Host_Name = \"pserver_01\" RETURN p, type(r)")
Collection<Host> getAllHosts();
}
The hourly relation info (type(r)) is not returning from the spring data neo4j repository.
Are node and relation definitions correct? Am I missing something here?
Any help would be appreciated...
Those empty strings you use are really odd. I don't think empty relationship-types work.
By default the direction is OUTGOING, if you want to see both you have to specify direction=BOTH in the annotation.
In your repository example you need to use a projection or #QueryResult to see the type(r) AS type column.
This is similar to what is discussed at Unable to use two Neo4j Instances with Spring boot/Spring data neo4j but I don't have two databases. I have downloaded the spring-data neo4j sample java application from the git repo and want to execute a dynamic query instead of executing a static query via repository interface.
I am facing an issue of null transaction manager.
Here's my interface :
public interface SearchRepositoryCustom {
Iterable<Movie> searchByCriteria();
}
Here's my custom repo impl:
#Repository
#Transactional
public class SearchRepositoryImpl implements SearchRepositoryCustom {
#Autowired
private SessionFactory sessionFactory;
#Override
public Iterable<Movie> searchByCriteria() {
String query = "MATCH (m:Movie)<-[r:ACTED_IN]-(a:Person) RETURN m,r,a LIMIT 10";
return sessionFactory.openSession().query(Movie.class, query, Collections.emptyMap());
}
}
Here's my configuration :
#Configuration
#EnableTransactionManagement
#EnableNeo4jRepositories(basePackages = "movies.spring.data.neo4j.repositories")
public class Neo4jPersistenceConfig {
#Bean
#ConfigurationProperties("spring.data.neo4j")
public Neo4jProperties neo4jProperties() {
return new Neo4jProperties();
}
#Bean
public org.neo4j.ogm.config.Configuration userConfiguration() {
return neo4jProperties().createConfiguration();
}
#Bean
public SessionFactory getSessionFactory() {
return new SessionFactory(userConfiguration(), "movies.spring.data.neo4j.domain");
}
#Bean
public Neo4jTransactionManager transactionManager() {
return new Neo4jTransactionManager(getSessionFactory());
}
}
Since I have only one TransactionManager and One SessionFactory (as I have only one Neo4j instance) I don't need to name the beans separately.
I am seeing the following exception :
org.neo4j.ogm.exception.core.TransactionManagerException: Transaction is not current for this thread
at org.neo4j.ogm.session.transaction.DefaultTransactionManager.rollback(DefaultTransactionManager.java:86) ~[neo4j-ogm-core-3.1.0.jar:3.1.0]
at org.neo4j.ogm.transaction.AbstractTransaction.rollback(AbstractTransaction.java:65) ~[neo4j-ogm-api-3.1.0.jar:3.1.0]
at org.neo4j.ogm.drivers.bolt.transaction.BoltTransaction.rollback(BoltTransaction.java:61) ~[neo4j-ogm-bolt-driver-3.1.0.jar:3.1.0]
at org.neo4j.ogm.transaction.AbstractTransaction.close(AbstractTransaction.java:144) ~[neo4j-ogm-api-3.1.0.jar:3.1.0]
at org.springframework.data.neo4j.transaction.Neo4jTransactionManager.doCleanupAfterCompletion(Neo4jTransactionManager.java:379) ~[spring-data-neo4j-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.cleanupAfterCompletion(AbstractPlatformTransactionManager.java:1007) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:793) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:714) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:532) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:304) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689) ~[spring-aop-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at movies.spring.data.neo4j.repositories.SearchRepositoryImpl$$EnhancerBySpringCGLIB$$d2631bcd.searchByCriteria(<generated>) ~[classes/:na]
at movies.spring.data.neo4j.controller.MovieController.advGlobal(MovieController.java:54) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_171]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_171]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_171]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_171]
Even if I actually go ahead and declare the name of the beans and mark the method transactional by specifying the name of the transactionManager, I still get the same error consistently.
Java version : 1.8
neo4j version : 3.4.6
What am I missing?
Gerrit is right. I'd like to add the two options we have here. We provide an injectable Session that is bound to the current thread and is integrated with Springs transactions. Just auto wire that instead of the SessionFactory and you're good to go with your solution. Please note that I'm using constructor injection as recommended throughout all Spring projects:
#Repository
#Transactional
class SearchRepositoryImpl implements SearchRepositoryCustom {
private final Session session;
public SearchRepositoryImpl(Session session) {
this.session = session;
}
#Override
public Iterable<ThingEntity> searchByCriteria() {
String query = "MATCH (t:ThingEntity) RETURN t LIMIT 10";
return session.query(ThingEntity.class, query, Map.of());
}
}
I have used another domain to create a concise example project, but the idea stays the same.
For a simple use case like that I fully agree with Gerrit and would use the #Query annotation on a declarative Spring Data Neo4j repository like this:
interface ThingRepository extends Neo4jRepository<ThingEntity, Long> {
#Query("MATCH (t:ThingEntity) RETURN t LIMIT 10")
public Iterable<ThingEntity> searchByCriteria();
}
The usage is the same, as demonstrated here:
#Component
class ExampleUsage implements CommandLineRunner {
private final ThingRepository thingRepository;
private final SearchRepositoryCustom searchRepositoryCustom;
public ExampleUsage(ThingRepository thingRepository, SearchRepositoryCustom searchRepositoryCustom) {
this.thingRepository = thingRepository;
this.searchRepositoryCustom = searchRepositoryCustom;
}
#Override
public void run(String... args) {
this.thingRepository.save(new ThingEntity(1));
this.thingRepository.save(new ThingEntity(2));
var things = this.searchRepositoryCustom.searchByCriteria();
things.forEach(System.out::println);
things = this.thingRepository.searchByCriteria();
things.forEach(System.out::println);
}
}
You'll find the complete application as a gist: Use Spring Data Neo4js injectable OGM Session. I have used Java 10 instead of 8 as we approaching EOL for Java 8, but that doesn't change the repository implementations. Apart from that, tested with Spring Boot 2.0.4, Spring Data Kay and OGM 3.1.0.
Edit: In regard to the comment: The injectable session is a proxy. The field itself is final, but the proxy opens sessions as needed and then delegates to it.
You are mixing up Neo4j-OGM‘s SessionFactory/Session and the #Transactional support of Spring (Data Neo4j). The latter will create a new transaction of which the OGM code is not aware and tries to create a fresh transaction.
If you use Spring Data Neo4j you can also define the query within your entity repository with a #Query annotated method.
The other solution would be to remove the #Transactional annotation in your service layer and create it manually if you plan to execute multiple operations (Not needed for one because OGM will create a transaction implicit if it does not exist).
If I run neo4j in server mode so it is accessible using the REST API, can I access the same neo4j instance with EmbeddedGraphDatabase-class?
I am thinking of a production setup where a Java-app using EmbeddedGraphDatabase is driving the logic, but other clients might navigate the data with REST in readonly mode.
What you are describing is a server plugin or extension. That way you expose your database via the REST API but at the same time you can access the embedded graph db hihgly performant from your custom plugin/extension code.
In your custom code you can get a GraphDatabaseService injected on which you operate.
You deploy your custom extensions as jars with your neo4j-server and have client code operate over a domain oriented restful API with it.
// extension sample
#Path( "/helloworld" )
public class HelloWorldResource {
private final GraphDatabaseService database;
public HelloWorldResource( #Context GraphDatabaseService database) {
this.database = database;
}
#GET
#Produces( MediaType.TEXT_PLAIN )
#Path( "/{nodeId}" )
public Response hello( #PathParam( "nodeId" ) long nodeId ) {
// Do stuff with the database
return Response.status( Status.OK ).entity(
( "Hello World, nodeId=" + nodeId).getBytes() ).build();
}
}
Docs for writing plugins and extensions.