Is it possible to change properties of a bean (defined for a service) and reload it when the application is running? - spring

I migrated a simple CRUD application developed in Java using OSGi to Grails using Spring. I converted all the REST resources to controllers and HTML pages to GSP views, keeping the rest of the Java code as such.
I have a DBService service, which helps connect to the DB and run queries on it, and a ProcessorService, which uses DBService to perform business operations.
I created beans for these services as follows:
beans = {
dbServiceBean(DBService, "test_db")
processorServiceBean(ProcessorService,ref("dbServiceBean"))
}
Everything is working fine with the above config.
Now, I want the application to be able to process multiple DBs (multi-tenant). I won’t know the name of the DB beforehand, however, so I can’t have a list of dbServiceBeans predefined.
Is it possible to rebuild/reload a bean with dynamically obtained values and reload the dependent beans as well when the application is running?

Grails already have the option to use multiple datasources.
You can change your DBService to get a connection from the datasources configured. If you just change it to a Groovy class and put it in grails-app/service you will get transactions and dependency injection by attribute name for free.

Related

Bind Spring datasource to JNDI

I have a spring boot based spring application that is deployed into an external tomcat instance.
The application creates few datasources. These datasources are added to entitymanager and transaction manager is setup accordingly.
However, recently we have integrated programmatically an ETL tool that works with JNDI datasources. The ask here is to bind the current spring datasources into the JNDI tree at startup.
I have tried to create an initial context post datasource bean creation and bind the datasources there, however, i do see a NoInitialContext exception being thrown.
How can i bind these spring datasources into the JNDI tree of the external tomcat? Appreciate the help!
Note: I cannot/am not allowed to edit the tomcat configuration as it is initialized from a PaaS template. So need to work on the approach of being able to bind to the JNDI tree from within the application.
AFAIK this is not possible. Take a look at the JEE spec:
The container must ensure that the application component instances have only read access to their naming context. The container must throw the javax.naming.OperationNotSupportedException from all the methods of the javax.naming.Context interface that modify the environment naming context and its subcontexts.
Jakarta EE Spec - Resources, Naming, and Injection
See this SO post has some interesting code examples if you want to play around.
IMHO you can achieve what you want by creating JNDI resources and passing these to the EntityManger/Spring. But that means that the configuration would exist outside of Spring completely. So this may not do what you want to do.

Facing issue while integrating CustomSpringUtilityJar in SpringBootApplication

I am having two spring applications and the details as follows:
1). Application One: This is a utility jar and its purpose is for CRUD operations on a specific table. We are using spring data jpa and hibernate for achieving the same. Since it is an utility this will not run alone
and it will be incuded as dependency to other applications. In this case we are using Application2 for that.
2) Application Two: This is a web service application made of Spring boot using yml configuration and also using Application One for CRUD operations on that table.
Issue: While running application Two(i.e. Spring Boot Application) , I am getting the error message related to the the components, repositories and entities of Application one,
as the bean is not registered in the configuration. Following are the approaches I used to solve thes:
Approach 1: In application 2, along with #SpringBootApplication annotation:
added Component Scan, Enity Scan and Enable JPA repositories and in these three annotations we have to include the package of both Application1 and Application2.
This scenario is working fine.
Note: I am not using Approach 1 as I have to make these three changes in every service applications which is going to use this utility jar. So decided to proceed with Approach2
Approach 2: Created an custom annotation like #UtilJar annotation and configuration class in Application1 and in that configuration class, added the three
annotations along with the configuration annotation.
In this case, while adding the custom annotation(created from Application1) in Application2, all the components and repositories registered in Application1 is registered and error is
showing related to the repositories registered in Application 2 . The only way I can solve on this is to give EnableJPARepository in Application2 which again similar to
Approach1.
So the root cause what I understood is because of #EnableJPARepository annotation given in the Application1 prompted for this error in Application2. Can anyone suggest me how to proceed on this.

Is it possible to access the spring context outside of a spring-boot application?

I am trying to create a standalone database application which can offer CRUD operations to other applications/modules...
I am aware of the need to create the entities and services used by the application in another artifact since you cannot depend on a spring-boot application alone.
But, can one get the runtime spring-configuration of a spring-boot application? So one can access a service that is deployed on my application?
For best through-put I am looking for a way to use services on a running spring-boot database application on the same JVM in order to minimise overhead...
RMI
What you want is technically it is possible using basic Java RMI (remote method invocation), you just register the bean as the implementation instance and share the interface between the two JVMs, either on localhost or even on different machines.
Spring even gives some additional support for this using RmiProxyFactoryBean, see Spring Remoting RMI article.
From above article, you can export it, using:
#Bean
RmiServiceExporter exporter(CabBookingService implementation) {
Class<CabBookingService> serviceInterface = CabBookingService.class;
RmiServiceExporter exporter = new RmiServiceExporter();
exporter.setServiceInterface(serviceInterface);
exporter.setService(implementation);
exporter.setServiceName(serviceInterface.getSimpleName());
exporter.setRegistryPort(1099);
return exporter;
}
and import it, using:
#Bean
RmiProxyFactoryBean service() {
RmiProxyFactoryBean rmiProxyFactory = new RmiProxyFactoryBean();
rmiProxyFactory.setServiceUrl("rmi://localhost:1099/CabBookingService");
rmiProxyFactory.setServiceInterface(CabBookingService.class);
return rmiProxyFactory;
}
Then you can use it in your application context based on your interface.
However I would not suggest to share beans like this because it has the same problem RMI has.
Shared library
Another way to do what you want is to create a shared library that can be included in other projects.
If all the project, which want to use it are Spring Boot application you can create a Spring Boot starter, see Spring documentation.
This way, other applications just have to add the dependency and they already has access to the beans in their application context, as well as the shared domain objects.
If other application use just regular Spring, they can just #Import your main configuration class.
If other apps are using Java, but not Spring, you can still use Spring inside, just provide a factory, which creates an internal Spring context.
REST service
If other applications are not written in Java, your best bet is to expose a REST interface for them to use the database applications.

Can I duplicate a web service for testing?

I have a REST web service exposed at http://server:8080/my-production-ws by JBoss (7). My spring (3.2) configuration has a datasource which points to a my-production-db database. So far so good.
I want to test that web service from the client side, including PUT/POST operations but I obviously don't want my tests to affect the production database.
Is there an easy way to have spring auto-magically create another web service entry point at http://server:8080/my-test-ws or maybe http://server:8080/my-production-ws/test that will have the exact same semantics as the production web service but will use a my-test-db database as a data source instead of my-production-db?
If this is not possible, what is the standard approach to integration testing in that situation?
I'd rather not duplicate every single method in my controllers.
Check the spring Profiles functionality, this should solve the problem. With it its possible to create two datasources with the same bean name in different profiles and then activate only one depending on a parameter passed to the the JVM.

Share spring container between test application and embedded tomcat

We are using cucumber-jvm to write an integration test layer in our application. One of the challenges we are finding is managing the database between the tests and the web application.
A typical scenario is that we want to persist some entities in a Given step of a scenario, then perform some actions on the user interface that may, in turn, persoist more entities. At the end, we want to clean the database. Because the cucumber-jvm tests are in one jvm and the web application is running in another jvm we cannot share a transaction (at least in a way of which I am aware) so the database must be cleaned manually.
My initial thought was to use an Embedded Tomcat server running off of an embedded in-memory database (HSQLDB) in the same JVM as the cucumber-jvm test. This way we might be able to share a single spring container, and by extension a single transaction, from which all objects could be retrieved.
During my initial tests it looks like Spring gets loaded and configured twice: once when the test starts and the cucumber.xml is read, and a second time when the embedded tomcat starts and the web application reads its applicationContext.xml. These appear to be in two completely separate containers because if I try to resolve an object in one container that is specified in the other container then it doesn't resolve. If I duplicate my configuration then I get errors about duplicate beans with the same id.
Is there a way that I can tell Spring to use the same container for both my test application and the embedded tomcat?
I'm using Spring 3.2.2.GA and Embedded Tomcat 7.0.39 (latest versions of both libraries).
Am I crazy? Do I need to provide more technical details? Apologies if I use some incorrect terminology.
Thanks
p.s. If my problem seems familiar to you and you can suggest an alternative solution to the one I am trying, please let me know!
Jeff,
It is normal that spring is loaded twice. There are two places where two spring contexts are created:
In the servlet container listener org.springframework.web.context.ContextLoaderListener that is configured in web.xml. This one reads its configuration from the file set by the context-param contextConfigLocation.
In the implementation of ObjectFactory provided by cucumber-spring plugin cucumber.runtime.java.spring.SpringFactory. This one reads its configuration from cucumber.xml.
The two spring contexts are totally different and their instances are kept in two different places. As a servlet context attribute for the former and kept by the JavaBackend for the latter.
When starting the embedded tomcat, it is possible to get access to the servlet context and thus set ourself the spring context used bt tomcat with the one from cucumber. But, spring has a special class called WebApplicationContext for context used in a servlet container. The cucumber SpringFactory on other hand creates its context through ClassPathXmlApplicationContext. So unless there is a way to specify the type of application context from the xml config, we will have to provide an ObjectFactory that shoots a WebApplicationContext.
What we can do is to have two web.xml. One for the normal and one for the test. For the test, we use our version of the ContexLoader listener.

Resources