Accessing Neo4j from Async method - java

My web application uses Neo4j as a data storage, and it uses Spring Data Neo4j 4 framework.
As suggested in the tutorial, my neo4j session is bound to my HTTP session:
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Session getSession() throws Exception {
return super.getSession();
}
I have an endpoint which runs a time-consuming query, and sends the result offline. I'd like to move this method to an #Async thread, but obviously I can not access my neo4j session from that thread.
What is the best practise to access neo4j repositories outside of the HTTP session without changing the scope of the "main" session bean?

I'm not sure about best practice but can't you just create another session from the sessionFactory#openSession() method? And pass that new session to another instance of neo4jOperations (or #Override the existing bean if are not using it) thus avoiding using the proxyScoped Neo4jConfiguration#getSession() method.
like so:
// note below assumes you are extending Neo4jConfiguration
// ...
// passing in your own non proxyScoped session.
// #Override existing neo4jTemplate #Bean passing in your own session
#Bean
#Override
public Neo4jOperations neo4jTemplate() throws Exception {
return new Neo4jTemplate(getSessionFactory().openSession());
}
// or create another neo4jTemplate instance that avoids getSession() proxyScope method usage in its constructor.
#Bean("nonProxyScopedNeo4jOperations")
public Neo4jOperations nonProxyScopedNeo4jTemplate() throws Exception {
return new Neo4jTemplate(getSessionFactory().openSession());
}
// ...
and using the custom neo4jOperations bean to perform your #Async logic
see Neo4jConfiguration:

I have ended up moving my neo4jSession to a thread scope. As our application is stateless, our session is only one request. And as every request is handled in a separate thread, thread scope was the easiest way.
I'd like to thank to the developer of https://github.com/devbury/spring-boot-starter-threadscope , made my life easier. :)

Related

Java EE 7 - How start a transaction from inside a container?

I'm using a Java EE 7 + GlassFish and need to perform some operation against a number of JPA entities from a stateless bean.
#Stateless
public class JobRunner
{
public void do()
{
for (Entity entity:facade.findAll())
{
///do some work against entity
}
}
}
This JobRunner bean is injected into the servlet and I invoke do() method from the web UI.
The issue is that all entities are being changed within one transaction so if one fails everything is rolled back what is not desirable. Is there a way to start and close a new transaction for each entity (i.e. for each iteration of the loop)?
I can write an external client and make a loop there calling a stateless bean for each entity but it's not something that completely works for me as I prefer to keep an app monolithic. Can I somehow manage transactions form inside a container?
Maybe JMS helps? If I implement a doer as message listener and will be sending a message for each entity, will it start a new transaction for each one?
#Stateless
public class JobRunner
{
public void do()
{
for (Entity entity:facade.findAll())
{
sendMessageToRealDoer(entity);
}
}
}
Create another bean, specifying #TransactionAttribute(TransactionAttributeType.REQUIRES_NEW), at method or bean level:
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
#Stateless
public class JobWork {
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void doWork(Entity entity) {
// do what you would do in the loop with the Entity
// this runs in a new transaction
}
}
I wish I could tell you that you only need to annotate a method of the same bean (JobRunner) and simply call it. This is not possible (EDIT)without workarounds - check comment from Steve C(/EDIT) because when calling methods of this object in EJBs and CDI beans the interceptors do not get called. Transactions are implemented with interceptors in both cases.
Some notes:
If the total duration of the operations in the loop is expected to be long, you will get a timeout in the outer transaction, that is implicitly started for the JobRunner stateless EJB. You will want to take measure that no "outer" transaction is started.
Sending the data to a queue will work too; but queues will process them asynchronously, meaning that the execution will return to the servlet calling JobRunner.do() most probably before all items have been processed.

Get the current Play session() in Java

I Have this class:
package ds;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import play.mvc.Http;
public class MyRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected String determineCurrentLookupKey() {
return Http.Context.current().session().get("currentDB");
}
}
But when I want to access into the Play session I have this error:
Caused by: java.lang.RuntimeException: There is no HTTP Context available from here.
at play.mvc.Http$Context.current(Http.java:34) ~[play_2.10-2.3.10.jar:2.3.10]
at play.mvc.Controller.session(Controller.java:72) ~[play_2.10-2.3.10.jar:2.3.10]
I also tried with HttpExecution.defaultContext() and his HttpExecutionContext but it cannot be cast to the Http.Context that is what I need.
I was thinking about get the request header but certainly I don't know how to handle it from my class and determine the session from the request
You can't access the Play Context in that tier cause is out of scope. In the documentation of the AbstractRoutingDatasource:
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSource.html
The main description of the class says:
Abstract DataSource implementation that routes getConnection() calls
to one of various target DataSources based on a lookup key. The latter
is usually (but not necessarily) determined through some
thread-bound transaction context.
So this class is suggesting that a way to get information of the current context should be using a Thread-Bound Transaction Context.
Now, Is PlayFramework Thread Context Safe? Reading Play documentation:
https://www.playframework.com/documentation/2.3.x/ThreadPools#Java-thread-locals
Java code in Play uses a thread local to find out about contextual
information such as the current HTTP request. Scala code doesn’t need
to use thread locals because it can use implicit parameters to pass
context instead. Threads locals are used in Java so that Java code can
access contextual information without needing to pass context
parameters everywhere.
So, if you are using a Java implementation, you can use ThreadLocals as context channel between components. Be careful if you create your own thread pools because there's a warning in the same documentation:
The default objects wrap the default user thread pool. If you want to
do your own threading then you should use the HttpExecution class’s
helper methods to get an ExecutionContextExecutor object yourself.
But that won't be a problem if you are not using custom thread pool in your app.
Said that, what you have to do is:
Define a ThreadLocalContext for the object that you want to use as router.
Put in the context. (You can do it in the controller, in the security controller if you are using an authorization framework like Deadbolt or even implementing a new request filter.)
Reading in the AbstractRoutingDataSource the ThreadLocal context.
Important! Don't forget to clean the Thread-Local or you can face a memory Leak.
Step 1:
public class RequestContext {
private static final ThreadLocal<String> contextHolder =
new ThreadLocal<String>();
public static void setRoutingKey(String key) {
contextHolder.set(key);
}
public static String getRoutingKey() {
return (String) contextHolder.get();
}
public static void clearRoutingKey() {
contextHolder.remove();
}
}
Step 2:
//Demostrative code, not tested, not even compiled
public static void myController() {
RoutingContext.setRoutingKey(Play.Context.request());
return bla;
}
Step 3:
#Override
protected Object determineCurrentLookupKey() {
String datasource = RoutingContext.getRoutingKey();
RoutingContext.clearRoutingKey();
return datasource;
}
Regards!

Guice Provider<EntityManager> vs EntityManager

I was trying to get simple webapp working with Guice and JPA on Jetty, using the persistence and servlet guice extensions.
I have written this Service implementation class:
public class PersonServiceImpl implements PersonService {
private EntityManager em;
#Inject
public PersonServiceImpl(EntityManager em) {
this.em = em;
}
#Override
#Transactional
public void savePerson(Person p) {
em.persist(p);
}
#Override
public Person findPerson(long id) {
return em.find(Person.class, id);
}
#Override
#Transactional
public void deletePerson(Person p) {
em.remove(p);
}
}
And this is my servlet (annotated with #Singleton):
#Inject
PersonService personService;
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String name = req.getParameter("name");
String password = req.getParameter("password");
String email = req.getParameter("email");
int age = Integer.valueOf(req.getParameter("age"));
Person p = new Person();
p.setAge(age);
p.setName(name);
p.setEmail(email);
p.setPassword(password.toCharArray());
logger.info("saving person");
personService.savePerson(p);
logger.info("saved person");
logger.info("extracting person");
Person person = personService.findPerson(p.getId());
resp.getWriter().print("Hello " + person.getName());
}
When I run this it works, and I get the name sent to the client, but when I look at the log I see that there is no DML generated for the insertion and selection from postgresql does not return any results, which means it wasn't really persisted.
I debugged through the code and I saw that JpaLocalTxnInterceptor called txn.commit().
Then I made a change to PersonServiceImpl and used Provider<EntityManager> instead of just EntityManager and it worked as expected. Now I don't really understand why and it's probably because I don't really understand the idea behind Provider.
On the Guice wiki page it says:
Note that if you make MyService a #Singleton, then you should inject Provider instead.
However, my PersonServiceImpl is not a #Singleton so I am not sure why it applies, perhaps it's because of the Servlet?
I would really appreciate if you could clear this out for me.
You need Provider<EntityManager> because Guice's built-in persistence and servlet extensions expect EntityManager to be request-scoped. By injecting a request-scoped EntityManager from a service held in a singleton servlet, you're making a scope-widening injection, and Guice won't store data from a stale, mismatched EntityManager.
Providers
Provider is a one-method interface that exposes a get() method. If you inject a Provider<Foo> and then call get(), it will return an instance created the same way as if you had injected Foo directly. However, injecting the Provider allows you to control how many objects are created, and when they are created. This can be useful in a few cases:
only creating an instance if it's actually needed, especially if the creation takes lots of time or memory
creating two or more separate instances from within the same component
deferring creation to an initialization method or separate thread
mixing scopes, as described below
For binding of X, Provider<X>, or #Provides X, Guice will automatically allow you to inject either X or Provider<X> directly. You can use Providers without adjusting any of your bindings, and Providers work fine with binding annotations.
Scopes and scope-widening injections
Broadly speaking, scopes define the lifetime of the object. By default, Guice creates a new object for every injection; by marking an object #Singleton, you instruct Guice to inject the same instance for every injection. Guice's servlet extensions also support #RequestScoped and #SessionScoped injections, which cause the same object to be injected within one request (or session) consistently but for a new object to be injected for a different request (or session). Guice lets you define custom scopes as well, such as thread scope (one instance per thread, but the same instance across injections in the same thread).
#Singleton public class YourClass {
#Inject HttpServletRequest request; // BAD IDEA
}
What happens if you inject a request-scoped object directly from within a #Singleton component? When the singleton is created, it tries to inject the instance relevant to the current request. Note that there might not be a current request, but if there is one, the instance will be saved to a field in the singleton. As requests come and go, the singleton is never recreated, and the field is never reassigned--so after the very first request your component stops working properly.
Injecting a narrow-scope object (#RequestScoped) into a wide scope (#Singleton) is known as a scope-widening injection. Not all scope-widening injections show symptoms immediately, but all may introduce lingering bugs later.
How Providers help
PersonService isn't annotated with #Singleton, but because you're injecting and storing an instance in a #Singleton servlet, it might as well be a singleton itself. This means EntityManager also has singleton behavior, for the same reasons.
According to the page you quoted, EntityManager is meant to be short-lived, existing only for the session or request. This allows Guice to auto-commit the transaction when the session or request ends, but reusing the same EntityManager is likely preventing storage of data any time after the first. Switching to a Provider allows you to keep the scope narrow by creating a fresh EntityManager on every request.
(You could also make PersonService a Provider, which would also likely solve the problem, but I think it's better to observe Guice's best practices and keep EntityManager's scope explicitly narrow with a Provider.)

Hibernate and Spring, load a collection that is in lazy mode

I'm using Spring to manage transaction in my service layer with #Transactional annotation.
This is fine for eager loaded collection, but is bad for lazy loading.
I cant use OSV pattern, my application is a standalone desktop client application.
So I thought about a solution, and this is what I tried to do:
public abstract class TransactionTask {
public TransactionTask() {
}
public abstract void job();
}
In my service I have:
#Transactional
public void doJob(Transactiontask tt){
tt.job();
}
And how I use it is:
myService.doJob(new TransactionTask() {
#Override
public void job() {
//lazy code here
}
});
I expect to see this code work, becouse session is open in doJob method(it is marked as transactional), but it doesn't work...the exception is no session or session was closed.
Why this code doesn't work, and how can I load a collection only when i need it?
I do not want to write a service with a specified method to load collection, that is not a solution.
Lazy Loading doesn't require just a session, it requires the session that your Entity is attached to. You need to attach the objects to the new session created by #Transactional before you try to access the lazy fields.
Also, while you obviously cannot use one of the available OSIV servlet filters, the 'pattern' is still valid. There has to be some definable scope that you can couple the lifecycle of a session to. (It may prove to be so large that there are other challenges making it not worth the effort, but it is still an option.)
E.g. you have User user object. It's detached that's why you got "the exception is no session or session was closed".
You can reread it from the dao user=userDao.findById(user.getId()) and then get lazy collections of the newly retrieved object.

Spring - "late binding" autowired beans

Let's say I have the following dependencies:
#Configuration
public class MyCfg {
// ...
#Bean
public Session session() {
return sessionFactory().getCurrentSession();
}
}
#Repository
#Transactional
public class MyRepo {
#Autowired
private Session session;
}
sessionFactory() is set up properly. If I inject SessionFactory instead of Session, it works just fine. However, if try and inject Session, it dies with an exception on container bootstrap because there is no session bound to thread.
Since the repository is #Transactional, I know that at run time there will be a session. How can I make it work, so that it injects AOP-initialized Session at run time, but does not try and resolve it when the repo is instantiated?
I would take a look at this bit of Spring documentation regarding bean scopes. Near the bottom they show how to use the #Scope annotation, which you will want to apply to your session() method in MyCfg. It sounds like you would want to use the 'request' value, which will create a new instance of this bean for each HTTP request coming in.
I will also suggest taking a look at the <aop:scoped-proxy/> element for configuration. It is mentioned a couple times in the documentation, and may be useful for what you are trying to do.
This approach will get you into a lot of trouble. Instead of injecting a Session, which you now automatically scopes as a singleton, you should inject the SessionFactory instead. Instances of Session acquired within a #Transactional annotated method will adhere to those transaction rules, eg:
#Transactional(readonly=true)
public List<Person> getPersons() {
Session session = sessionFactory.getCurrentSession();
//find those darn people.
}
#Autowired
private SessionFactory sessionFactory;

Categories

Resources