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).
Related
I want to write some kind of unit test which depends on Spring Security.
For example, I have some service method which uses some repository and marked with #PreAuthorize annotation. Repository I can mock with Mockito, there is no problem. Also I can mock Security Context by #WithSecurityContext annotation. But when I run test, the #PreAuthorize annotation is just ignored. Of course I can run that test with #SpringBootTest annotation as an integration test and in this case the Security Context is up but this way is heavy and slow.
Is there a way to run unit test with only Spring Security Context raised?
UPDATE
Made an example of such kind of test. Thanks to #Sam Brannen for giving right direction.
#ActiveProfiles("method-security-test")
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {ExampleService.class, ExampleServiceTest.MethodSecurityConfiguration.class})
public class ExampleServiceTest {
private ExampleService service;
#Autowired
public void setService(ExampleService service) {
this.service = service;
}
#Test
#WithMockUser(username = "john_doe")
public void testAuthenticated() {
String actualMessage = service.example();
Assert.assertEquals("Message of john_doe", actualMessage);
}
#Test(expected = AuthenticationException.class)
public void testNotAuthenticated() {
service.example();
Assert.fail();
}
#TestConfiguration
#EnableGlobalMethodSecurity(prePostEnabled = true)
static class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
}
}
#Service
class ExampleService {
#PreAuthorize("isAuthenticated()")
String example() {
Principal principal = SecurityContextHolder.getContext().getAuthentication();
return "Message of " + principal.getName();
}
The #PreAuthorize annotation from Spring Security will only be honored if Spring Security proxies your component (e.g., service bean).
The simplest way to make that happen is by annotating an #Configuration class with #EnableGlobalMethodSecurity(prePostEnabled = true), having your component registered as a bean (e.g., via component scanning or an #Bean method), and including your AuthenticationManager setup.
You can then create a focused integration test using #ContextConfiguration (without Spring Boot testing support) to load an ApplicationContext from your #Configuration class. And you can use #Autowired to get access to your proxied component which will be advised with the #PreAuthorize security check support.
You might find this old blog post useful as well for background information: https://spring.io/blog/2013/07/04/spring-security-java-config-preview-method-security/
Using NetBeans, I have successfully run the shopping-cart example presented in the Java EE 6 tutorial on the Oracle website. It's an EAR with two modules: an EJB module and an application client module. I have cut short some details of the code in order to focus on the confusions I am facing. Firstly, below is the code.
The remote interface in the EJB module
package cart.ejb;
import cart.util.BookException;
import java.util.List;
import javax.ejb.Remote;
#Remote
public interface Cart {
public void initialize(String person) throws BookException;
public void initialize(
String person,
String id) throws BookException;
public void addBook(String title);
public void removeBook(String title) throws BookException;
public List<String> getContents();
public void remove();
}
The stateful session bean in the EJB module
package cart.ejb;
import cart.util.BookException;
import cart.util.IdVerifier;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.Remove;
import javax.ejb.Stateful;
#Stateful
public class CartBean implements Cart {
List<String> contents;
String customerId;
String customerName;
public void initialize(String person) throws BookException {
.................
}
public void initialize(
String person,
String id) throws BookException {
.....................
}
public void addBook(String title) {
contents.add(title);
}
public void removeBook(String title) throws BookException {
.................
}
public List<String> getContents() {
return contents;
}
#Remove()
public void remove() {
contents = null;
}
}
The client in the application client module
package cart.client;
import java.util.Iterator;
import java.util.List;
import javax.ejb.EJB;
import cart.ejb.Cart;
import cart.util.BookException;
public class CartClient {
#EJB
private static Cart cart;
public CartClient(String[] args) {
}
public static void main(String[] args) {
CartClient client = new CartClient(args);
client.doTest();
}
public void doTest() {
..................
}
}
Now my confusions begin!!
Confusion-1: The tutorial says it is a remote client. But it is not! It is packaged as a module within the same EAR as the EJB module. Wouldn't #Local be more appropriate?
Confusion-2: If glassfish does consider it a remote communication between the client and the session bean, will the client's container supply a fake instance of the session bean (i.e. proxy) representing the actual instance of the session bean in the EJB container, just like in a "real" remote case? I mean, does remote mean remote, not matter what?
Confusion-3: I am thinking about creating a client in a non-EE environment and then getting it to communicate with the bean from outside that EAR, to get a taste of "real" remote. So, if I launch Eclipse and create a Java SE 7 client program by copying contents from the client in EAR, are these the changes I have to make?
Replace dependency injection
#EJB
private static Cart cart;
with JNDI
private static Cart cart = (Cart) InitialContext.lookup("java:global/cart/cart-ejb/CartBean/Cart");
and add the Cart interface's source code in the Java Standard Edition client project.
Confusion-4: I am unable to find the location of that EAR file. On NetBeans, I can see under cart there are two jar files. But, where is the EAR file? I deployed the project by directly opening it from C:\glassfish-4.1.1\docs\javaee-tutorial\examples\ejb\cart with NetBeans.
#Local would be enough yes, apparently they were lazy with the demonstration and chose to package it all in one. Regardless, your deployment unit is the scope of you #Local interface, so 2 different EARS in the same container could not access each-others #Local interfaces
Yes and no: the outside behavior will be completely identical to a real remote case (with serialization and proxying of objects), but how this is really handled behind the scenes depends on the container implementation. To my knowledge most containers will leverage the fact that even though it's #Remote it is located in the same JVM so for example they will not make an actual remote socket connection.
3.
You need the remote interfaces of your beans
Configure glassfish to allow remote calls on a specific port with specific credentials
Do a JNDI lookup against this setup
I am new to Spring AMQP and want to use annotation based configuration for both producers and consumers using latest spring amqp 1.5.4 ,
Is there any pseoudo code available for configuration which does the logic for creating connection or #Queue etc.
Probably the quickest way to get started would be use Spring Boot - boot will create all the beans you need (connecting to localhost by default but easily overridable with properties).
You can also look at some of the Spring AMQP test cases.
Have a class annotated with #Configuration in Spring Boot which can provide you with annotation based bean definition :
Here is a sample :
#Configuration
public class QueueConfig {
#Bean
public AmqpAdmin amqpAdmin() {
return new RabbitAdmin(connectionFactory());
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory());
rabbitTemplate.setExchange("myQueue");
return rabbitTemplate;
}
#Bean
Queue rabbitQueue() {
return new Queue(WORKERS_QUEUE_NAME, true, false, false, null);
}
}
I am currently building a Dropwizard + Guice + Jersey-based application where the database access is being handled by JDBI for the time being.
What I am trying to achieve is to have your typical enterprise architecture, where Resources access Service classes accessing a DAO class that in turn accesses the database. It would be nice to get all this wired up in a proper DI way, although I guess I can build my object graph in the run() method of the application if all else fails.
So, I'm running into this problem that has been mentioned here before: Getting a DBIFactory requires both the Environment and the Configuration, which somehow need to be available at the time when Guice does its injection magic and not at run()-time.
Being a Dropwizard and Guice noob, what I've managed to put together so far is that I need a Provider for my DAO objects, something to the tune of
public class UserDAOProvider implements Provider<UserDAO> {
#Inject
Environment environment;
#Inject
Configuration configuration;
#Override
public UserDAO get() {
final DBIFactory factory = new DBIFactory();
final (MyConfiguration) config = (MyConfiguration) configuration;
DBI jdbi = null;
try {
jdbi = factory.build(environment, config.getDataSourceFactory(),
"mysql");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return jdbi.onDemand(UserDAO.class);
}
}
Registering this as a singleton provider should let me then inject the UserDAO into my Services.
Now, how do we actually get the environment injected into the Provider? Currently I am stuck at Guice complaining about not finding a suitable constructor for the Environment, so it is trying to instantiate it and not grab it from Dropwizard itself.
It seems like this is doable; there is the dropwizard-guice package whose DropWizardEnvironmentModule is, I think, what I need. But I feel like I'm just missing some piece of the puzzle here for an understanding of how to put things together. I've not managed to find a complete working example so far...
I had the same issue as OP but using Hibernate rather than JDBI. My simple solution is applicable to JDBI, though - just switch DBIFactory for SessionFactory.
First add an injection provider for a singleton SessionFactory in your Guice module:
public class MyModule extends AbstractModule {
private SessionFactory sessionFactory;
#Override
protected void configure() {
}
#Provides
SessionFactory providesSessionFactory() {
if (sessionFactory == null) {
throw new ProvisionException("The Hibernate session factory has not yet been set. This is likely caused by forgetting to call setSessionFactory during Application.run()");
}
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
}
You need to set the singleton SessionFactory from your application's run() method. In your case, using JDBI, this is where you would create and configure your DBIFactory before handing it over to the Guice module:
public void run(MyConfiguration configuration, Environment environment) {
myModule.setSessionFactory(hibernateBundle.getSessionFactory());
...
}
Now SessionFactory can be injected wherever it is needed. I now use implicit binding for my DAO classes by just annotating the constructor with #Inject and injecting the SessionFactory singleton. I don't explicitly create providers for DAO classes:
#Singleton
public class WidgetDAO extends AbstractDAO<App> {
#Inject
public WidgetDAO(SessionFactory factory) {
super(factory);
}
public Optional<Widget> findById(Long id) {
return Optional.fromNullable(get(id));
}
...
}
Now I can inject my DAO singleton instances into resources:
#Path("/widgets")
#Produces(MediaType.APPLICATION_JSON)
public class WidgetsResource {
private final WidgetDAO widgetDAO;
#Inject
public WidgetsResource(WidgetDAO widgetDAO) {
this.widgetDAO = widgetDAO;
}
...
}
Note that this approach follows the Guice recommendation of injecting direct dependencies only. Don't try to inject Envrionment and Configuration just so that you can create a DBI factory - inject the prebuilt DBI factory itself.
This is how I use Guice with Dropwizard. Inside your run() method add the line
Guice.createInjector(new ConsoleModule());
You cannot inject Environ
Create the class ConsoleModule
public class ConsoleModule extends AbstractModule {
//configuration and env variable declaration
public ConsoleModule(ConsoleConfiguration consoleConfig, Environment env)
{
this.consoleConfig = consoleConfig;
this.env= env;
}
protected void configure()
{
//You should not inject Configuration and Environment in your provider since you are mixing
//dropwizard framework stuff with Guice.Neverthless you will have to bind them in the below order
bind(Configuration.class).toInstance(consoleConfig.class);
bind(Environment.class).toInstance(env.class);
bind(UserDAO.class).toProvider(UserDAOProvider.class).in(Singleton.class);
}
}
We have the same configuration (dw-jdbi-guice) and also an abstract 'base' Application class which complicates things even more.
Since a lot of things happen during run method, and many things depend on the configuration objects we ended up creating the injector in the run method. But since we need objects from bootsrap also (e.g. ObjectMapper), we ended up having a List<Module> field in the Application class. Not the prettiest solution but can handle variety of scenarios.
We have an application with a plugin which contains a service:
public class TaskService {
public void doSomething( Task task ) {
// do something with task
task.save();
}
}
This works fine.
For our "special" customer with his special requirements we have a second application which contains the plugin from the first application and another plugin with a special service for this customer which extends the original service and overrides some methods:
public class SpecialTaskService extends TaskService{
#Override
public void doSomething( Task task ) {
// do something special with task
task.save();
}
}
In every place in the second application where taskService is injected we want to have the SpecialTaskService now (also in the plugin from the first application). So we have added the special service to the resources.groovy under grails-app/conf/spring:
beans = {
taskService( SpecialTaskService )
}
But now we get an HibernateException when we call "task.save()" in the special service:
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
We know that we could inject a SessionFactory into the SpecialService, but when we call sessionFactory.currentSession we get the same Exception.
The exception also occurs when we configure a service in resources.groovy which does not extend another one.
Is there a way to make the special service some kind of "hibernateSessionAware" so that we can call save() and merge() on domain objects?
The original service is transactional, so it keeps a Hibernate session open for the duration of the method call (unless one is already active and it has joined that). So you need to make yours transactional too since you're just telling Spring to create a plain new instance with taskService(SpecialTaskService)
The simplest thing to do is annotate the class (or individual methods if you prefer):
import org.springframework.transaction.annotation.Transactional
#Transactional
class SpecialTaskService extends TaskService {
#Override
void doSomething(Task task) {
// do something special with task
task.save()
}
}
but you can also wrap code blocks or entire methods in withTransaction blocks:
class SpecialTaskService extends TaskService {
#Override
void doSomething(Task task) {
Task.withTransaction { status ->
// do something special with task
task.save()
}
}
}