I have a problem connecting to a neo4j server from Java EE.
I use :
- neo4j 3.0.1 in server mode on localhost
- Weblogic 12.1.3
- JEE 7
- neo4j-ogm-core 2.0.3
- neo4j-ogm-bolt-driver 2.0.3
My Maven dependencies :
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-core</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-bolt-driver</artifactId>
<version>2.0.3</version>
</dependency>
My ogm.properties in resources/META-INF :
#Driver, required
driver=org.neo4j.ogm.drivers.bolt.driver.BoltDriver
#URI of the Neo4j database, required. If no port is specified, the default port 7687 is used. Otherwise, a port can be specified with bolt://neo4j:password#localhost:1234
URI=bolt://neo4j:xxxxxx#localhost
#Connection pool size (the maximum number of sessions per URL), optional, defaults to 50
connection.pool.size=150
#Encryption level (TLS), optional, defaults to REQUIRED. Valid values are NONE,REQUIRED
encryption.level=NONE
#Trust strategy, optional, not used if not specified. Valid values are TRUST_ON_FIRST_USE,TRUST_SIGNED_CERTIFICATES
trust.strategy=TRUST_ON_FIRST_USE
I use a EJB Singleton to define Session Factory :
#Singleton
public class Neo4jSessionFactory {
private SessionFactory sessionFactory;
#PostConstruct
public void init() {
sessionFactory = new SessionFactory("com.toto.poc.ejb.data.access");
}
/**
* Get neo4j session
* #return the session
*/
public Session getNeo4jSession() {
return sessionFactory.openSession();
}
}
it is in "com.toto.poc.ejb.data.access" package.
I have a EJB to define the business method in want to call :
#Stateless
public class TopologyBusiness {
private #EJB Neo4jSessionFactory neo4jSessionFactory;
public Iterable<Map<String, Object>> getApplication(String irt) {
String query = "MATCH (a:Application) WHERE a.irt = '" + irt + "' RETURN a";
Session session = neo4jSessionFactory.getNeo4jSession();
return session.query(query, Collections.emptyMap());
}
}
But when my client call TopologyBusiness, the init (PostConstruct) method of Neo4jSessionFactory is called, and an error occur :
com.oracle.pitchfork.interfaces.LifecycleCallbackException: Failure to invoke public void com.toto.poc.ejb.data.access.Neo4jSessionFactory.init() on bean class class com.toto.poc.ejb.data.access.Neo4jSessionFactory_wi88u8_Impl with args: null
at com.oracle.pitchfork.inject.Jsr250Metadata.invokeLifecycleMethod(Jsr250Metadata.java:379)
at com.oracle.pitchfork.inject.Jsr250Metadata.invokeLifecycleMethods(Jsr250Metadata.java:352)
at com.oracle.pitchfork.intercept.InterceptionMetadata.invokeLifecycleMethods(InterceptionMetadata.java:399)
at weblogic.ejb.container.injection.EjbComponentCreatorImpl.invokePostConstruct(EjbComponentCreatorImpl.java:55)
at weblogic.ejb.container.manager.SingletonSessionManager.constructAndInitBean(SingletonSessionManager.java:330)
Truncated. see log file for complete stacktrace
Caused By: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.oracle.pitchfork.inject.Jsr250Metadata.invokeLifecycleMethod(Jsr250Metadata.java:377)
Truncated. see log file for complete stacktrace
Caused By: java.lang.RuntimeException: org.neo4j.ogm.exception.ServiceNotFoundException: Resource: zip:C:/oracle/wls/12.1.3/user_projects/domains/CMDB/servers/LocalServer/tmp/_WL_user/poc-cdb-ear_ear/a8qjfi/poc-cdb-ejb-1.0-SNAPSHOT.jar!/com/toto.poc/ejb/data/access
at org.neo4j.ogm.ClassUtils.getUniqueClasspathElements(ClassUtils.java:178)
at org.neo4j.ogm.scanner.ClassPathScanner.getUniqueClasspathElements(ClassPathScanner.java:158)
at org.neo4j.ogm.scanner.ClassPathScanner.scan(ClassPathScanner.java:130)
at org.neo4j.ogm.metadata.DomainInfo.load(DomainInfo.java:316)
at org.neo4j.ogm.metadata.DomainInfo.<init>(DomainInfo.java:67)
Truncated. see log file for complete stacktrace
Caused By: org.neo4j.ogm.exception.ServiceNotFoundException: Resource: zip:C:/oracle/wls/12.1.3/user_projects/domains/CMDB/servers/LocalServer/tmp/_WL_user/poc-cdb-ear_ear/a8qjfi/poc-cdb-ejb-1.0-SNAPSHOT.jar!/com/toto.poc/ejb/data/access
at org.neo4j.ogm.service.ResourceService.resolve(ResourceService.java:53)
at org.neo4j.ogm.ClassUtils.getUniqueClasspathElements(ClassUtils.java:175)
at org.neo4j.ogm.scanner.ClassPathScanner.getUniqueClasspathElements(ClassPathScanner.java:158)
at org.neo4j.ogm.scanner.ClassPathScanner.scan(ClassPathScanner.java:130)
at org.neo4j.ogm.metadata.DomainInfo.load(DomainInfo.java:316)
Truncated. see log file for complete stacktrace
The root cause seems to be :
Caused By: org.neo4j.ogm.exception.ServiceNotFoundException: Resource: zip:C:/oracle/wls/12.1.3/user_projects/domains/CMDB/servers/LocalServer/tmp/_WL_user/poc-cdb-ear_ear/a8qjfi/poc-cdb-ejb-1.0-SNAPSHOT.jar!/com/toto.poc/ejb/data/access
I have no idea why it wants to look into a zip in the classpath ...
Thank for your help !
I can finally resolve my problem, I browsed odd forums and found someone who had similar issue on JBoss.
So I managed to transpose this solution to Weblogic.
First, you need to create your own resource resolver to provide the way to deal with "zip".
Overwrite ResourceResolver and implements resolve method like below :
public class Neo4jResourceResolver implements ResourceResolver {
#Override
public File resolve(URL resource) throws Exception {
switch (resource.getProtocol()) {
case "file":
return new File(resource.toURI());
case "jar":
case "zip":
String jarPath = resource.getPath().substring(0, resource.getPath().indexOf("!"));
return new File(jarPath);
default:
return null;
}
}
}
Then create a file named "org.neo4j.ogm.classloader.ResourceResolver" in path "src/main/resources/META-INF/services".
This file contains only one line, the path to your custom ResourceResolver class :
com.toto.poc.core.access.ucmdb.Neo4jResourceResolver
And it's magic, it works !!!
Neo4j will now explore the jar archive, found your NodeEntities and map it to your graph :)
Related
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).
After migrating to Spring security 4.2.2 (from 3.1), I am hitting the following error in LDAP user search:
java.lang.ClassCastException: com.sun.jndi.ldap.LdapCtx cannot be cast to org.springframework.ldap.core.DirContextAdapter
at org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleEntryInternal(SpringSecurityLdapTemplate.java:345)
at org.springframework.security.ldap.SpringSecurityLdapTemplate$3.executeWithContext(SpringSecurityLdapTemplate.java:318)
at org.springframework.ldap.core.LdapTemplate.executeWithContext(LdapTemplate.java:817)
at org.springframework.ldap.core.LdapTemplate.executeReadOnly(LdapTemplate.java:803)
at org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleEntry(SpringSecurityLdapTemplate.java:316)
at org.springframework.security.ldap.search.FilterBasedLdapUserSearch.searchForUser(FilterBasedLdapUserSearch.java:127)
...
...
I would appreciate any help in resolving the issue. I am using OpenLDAP server for the test if it makes any difference.
Thanks,
Raghu
Old question, but I had the same issue and manage to solve it somehow, so I thought I should share.
I'm using Spring Security 4.2.3 and I had something similar:
public AdvisorResponse getAdvisorInfo(final String uid) {
return cdsLdapTemplate.searchForObject(
createCriteria(uid), this::mapAdvisorResponse
);
}
private AdvisorResponse mapAdvisorResponse(final Object ctx) {
final DirContextAdapter context = (DirContextAdapter) ctx;
final AdvisorResponse advisor = new AdvisorResponse();
advisor.setUid(context.getStringAttribute("uid"));
return advisor;
}
private ContainerCriteria createCriteria(final String uid) {
return query()
.base("ou=people")
.countLimit(1)
.searchScope(SUBTREE)
.timeLimit(TIMEOUT)
.where("uid").is(uid);
}
It used to work flawlessly until I had to run it in an #Async thread, then I started to have the same class cast exception.
As suggested here the error seems to be due to a different classloader used in the thread. My solution was to force the correct classloader into the execution thread:
public AdvisorResponse getAdvisorInfo(final String uid) {
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
return cdsLdapTemplate.searchForObject(
createCriteria(uid), this::mapAdvisorResponse
);
}
I've written an HttpServlet which gets deployed to a Wildfly container in a WAR file. The servlet looks like this:
public class MyCallback extends HttpServlet {
#Inject
#Any
private Event<MyEvent> event;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
String eventName = request.getParameter("EVENT_NAME");
MyEvent e = new MyEvent(eventName);
event.fire(e);
}
}
I also created a beans.xml file (as detailed here) and placed it in my WEB-INF directory. Unfortunately, I still get a NullPointerException when the code tries to execute the event.fire(e) line, which suggests to me the injection isn't working.
What am I doing wrong?
Try adding at least one CDI bean (it doesn't have to do anything), e.g.
#ApplicationScoped
public class JustABean { }
I had similar issue on Wildfly beta, it seems without a single "normal" bean CDI engine just wouldn't kick in.
Also Wildfly ships CDI 1.1 where beans.xml is optional.
I have a Java EE 6 War project containing the following:
An EJB declared as so (it's also a JAX-RS Service):
#Path("/booksList")
#Produces("application/json")
#Stateless
#LocalBean
#Local(BooksListEJB.class)
public class BooksListEJBImpl implements BooksListEJB
A WebComponent declared as so:
#WebServlet(urlPatterns="/initDbData")
public class DataInitListener extends HttpServlet {
#EJB
private BooksListEJB booksListEJB;
An empty beans.xml file in the WEB-INF folder
When I deploy it in WebLogic 12c, I get the following error:
<Warning> <weblogic.jaxrs.onwls.deploy.ejb.provider.EJBComponentProviderFactory> <BEA-000000> <An instance of EJB class com.shivandragon.jee6TestService.ejb.impl.BooksListEJBImpl could not be looked up using simple form name. Attempting to look up using the fully-qualified form name.
javax.naming.NameNotFoundException: While trying to look up comp/env/BooksListEJBImpl in /app/webapp/jee6Test-service-0.1-SNAPSHOT.war/2039754748.; remaining na
me 'comp/env/BooksListEJBImpl'
at weblogic.jndi.internal.BasicNamingNode.newNameNotFoundException(BasicNamingNode.java:1180)
at weblogic.jndi.internal.ApplicationNamingNode.lookup(ApplicationNamingNode.java:146)
at weblogic.jndi.internal.WLEventContextImpl.lookup(WLEventContextImpl.java:253)
at weblogic.jndi.internal.WLContextImpl.lookup(WLContextImpl.java:426)
at weblogic.jndi.factories.java.ReadOnlyContextWrapper.lookup(ReadOnlyContextWrapper.java:45)
Truncated. see log file for complete stacktrace
I've looked similar questions, and found the suggestion to add #ManagedBean to the servlet. Tried that but had the same error.
My question is:
Shouldn't this work, am I misusing some Java EE 6 directive/standard?
In EJB 3.1 have been added new Bean view - LocaBean. You can develop a bean without need implement any inerfaces. That beans view is "no-interface view", annotated with #LocalBean and injected by classname. There are beans that implemented some local interfaces and has "local view" and should be injected via local interface. In your code you mixed no-interface view bean and local view bean. You should delete the #LocalBean annotation as #Sam answered.
Updated
I test it on WebLogic Server 12.1.1.0.
Create a simple interface with one method:
package ejbrest;
public interface SessionEJBLocal {
public String hello();
}
Then create a EJB with the RESTful annotations:
package ejbrest;
// ... imports
#Path("/booksList")
#Produces("application/json")
#Stateless
#Local(SessionEJBLocal.class)
public class SessionEJBBean implements SessionEJBLocal {
public SessionEJBBean() {
}
#Override
#GET
public String hello() {
return "Hello, world";
}
}
The deployment descriptor, web.xml (you can see it does not have any servlet definitions):
<?xml version = '1.0' encoding = 'UTF-8'?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
</web-app>
You can create a servlet for the local bean injection demo:
package ejbrest;
// ... imports
#WebServlet(name = "DemoServlet", urlPatterns = { "/demoservlet" })
public class DemoServlet extends HttpServlet {
private static final String CONTENT_TYPE = "text/html; charset=UTF-8";
#EJB
private SessionEJBLocal ejb;
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType(CONTENT_TYPE);
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head><title>DemoServlet</title></head>");
out.println("<body>");
out.println("<p>The servlet has received a GET. This is the reply: " +
ejb.hello() + "</p>");
out.println("</body></html>");
out.close();
}
}
After deployment you can try call your RESTful service by url:
http://[host]:[port]/[appcontext]/resources/booksList
Response:
Hello, world
Also, your demo servlet will be accessable by url:
http://[host]:[port]/[appcontext]/demoservlet
Response:
The servlet has received a GET. This is the reply:Hello, world
By default Oracle WebLogic Server use resources as link on Jersey servlet. Please read the official documentation for informs about all supported deployments variants.
I am using google guice as DI framework to bind my interfaces to the different Implementation. The server operates normally but whenever i try to call the Resource class the following error occurs. It is not that the binding never works but most of time it fails. I don't know what i am missing . I've given some of my source code below.
ModuleClass
public class ABCModule extends AbstractModule {
#Override
protected void configure() {
bind(IConnection.class).to(ABCConnection.class);
}
Resource class where injection is done
public class Resource {
Logger log = LoggerFactory.getLogger(Resource.class);
private final IContact contact;
private final IContactFolder contactFolder;
#Inject
public MLContact(IConnection con, IContactFolder contactFolder) {
this.contact = contact;
this.con = con;
this.contactFolder = contactFolder;
}
//Resource class methods
..
......
}
I am using guice-3.0.jar
EDIT 1:
INFO: Initiating Jersey application, version 'Jersey: 1.17 01/17/2013 04:27 PM' Aug 8, 2013 9:54:59 AM com.sun.jersey.spi.inject.Errors processErrorMessages
SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
SEVERE: Missing dependency for constructor public com.motilink.server.services.MLContact(com.motilink.connector.IContact,com.motilink.connector.IConnection,com.motilink.connector.IContactFolder) at parameter index 0
SEVERE: Missing dependency for constructor public com.motilink.server.services.MLContact(com.motilink.connector.IContact,com.motilink.connector.IConnection,com.motilink.connector.IContactFolder) at parameter index 1