Spring Beans Created Multiple Times - java

I am just new to Spring and facing a design problem. The problem is i have a maven multiple module projects. Project Structures goes as follows.
module-backoffice - Packaging(war)
module-ws - Packaging(war)
module-dao - Packaging(jar)
module-shared - Packaging(jar)
In "module shared "all my service class and in "module-dao" all dao related codes exists.
module-shared and module-dao is in the build path of module-backoffice and module-ws.
Now Problem is when i run module-backoffice war and module-ws war Spring creates two instance of Service class beans and dao class beans.
What should i do so that one instance of service class and dao class will be created and shared among multiple war ? Please help. Thanks you.
Code in Module-Shared: Below is Service factory class
#org.springframework.stereotype.Service
public class Services {
#Autowired
private List<Service> services;
private static final Map<Class<?>, Service> serviceCache = new ConcurrentHashMap<Class<?>, Service>();
#PostConstruct
public void initServiceCache() {
services.forEach(service -> serviceCache.put(service.getClass(), service));
services = null;
}
public static Bootstrap bootstrap() {
return (Bootstrap) serviceCache.get(Bootstrap.class);
}
And the service class is :
#Component
public class Bootstrap implements Service {
public Bootstrap() {
System.out.println("Bootstrap");
}
}
When i run tomcat deploying two wars then in console two times "Bootstrap" printed. How do i restrict that.?

The purpose of web container like tomcat is to able to run applications independently so they can be started and stopped without affecting each other. In case you think there can be multiple future applications will also require the same service, you can make a separate application and expose an API for the operations.

Related

unable to autowire custom spring boot library project to my application

Importing custom spring-boot library jar to my application, and autowiring show the following error when I run the application
Parameter 0 of constructor in com.dilla.de.orca.addresssvc.service.TestScheduler required a bean of type 'com.dilla.de.orca.flowersvc.service.FlowerServiceImpl' that could not be found.
The Library module has following packages
Configuration
FlowerServiceConfiguration - create beans for Jaxb2Marshaller, WebServiceTemplate,
webserviceMessageSender
Model
Service
FlowerService (an interface no annotation)
FlowerServiceImp implements the interface and calls FlowerAdapter
FlowerAdapter (call external webservice)
Src/main/resources
Application.properties define external webservice url, and related properties
FlowerSvcLibApplication.java
public static void main(final String[] args) {
SpringApplication.run(FlowerSvcLibApplication.class, args);
}
I was autowiring the Flower Service interface as follows, in my application to test functionality
of library jar
#Component
public class MyFlowerService {
private FlowerService service;
#Autowired
public MyFlowerService(final FlowerService service) {
this.service = service;
}
I got the error I posted earlier. I did do more research, and one suggestion was creating “own auto-configuration”, but I still did not understand. How do I create autoConfiguration class to handle to autowire my library class, and also how does client using my library provide application property values. Currently, I hard coded actual values for example a webservice url, and now client can change this to be test or prod, and to do that how does my library module setup should be?
Please check #ComponenetScan & make sure that it has package path something like this “com.dilla.de.orca”

How to register beans that can be shared by multiple WARs (Spring boot + Tomcat)

I am developing an application with Spring boot and deploying multiple WARs to an external tomcat.
I want to register a shareable bean with these WARs when tomcat starts.
I use: Java8, Spring boot(2.5.5) and Tomcat9
Bean I want to be shared by Apps:
public class TestData {
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
SampleApp(There are multiple apps that Autowire an instance of TestData in the same way):
#SpringBootApplication
#RestController
public class App extends SpringBootServletInitializer {
#Autowired
TestData testData;
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(App.class);
}
#RequestMapping(value = "SampleApp1", method = RequestMethod.GET)
public String getUsername() {
return testData.getUsername();
}
}
I think your question is about sharing the data of one singleton Bean between different application containers deployed in the same webserver.
This is not possible for security reasons. Each deployed application has its own classloader and hence cannot easily access beans residing in the memory of the other containers.
I suggest you add a microservice that is accessed by the different deployed applications and use that instead. It can easily be accessed via e.g OpenFeign as a client and almost looks like a bean with getters and setters if done right.
There are several approaches to your problem, solving it on different levels.
Create a shared library with required stuff and install it to Tomcat server. "Install" normally means to put assemled jars to $CATALINA_HOME/lib directory or similar. Class Loader HOW-TO can give you more details. Not sure how it works with shared instances of classes, but it's definitely possible to share classes (factories, for example) and configurations through shared library. With shared configuration and factories you can at least produce identical beans (not necessary the same, but it seems enough for you case).
Define a JNDI resource shared between all applications. Of course, this requires your class to be accessible by Tomcat, so you first have to go through option 1. This way you can have "published" instance of a class shared between all applications. Details can be found at JNDI Resources HOW-TO. With this approach you can inject published resource with #Resource annotation.
Create a microservice (as one of other answers suggests) that can provide you data and features and access it from your application. This gives you maximum flexibility but also brings complexity requiring actual communication to the microservice.
Obviously, options 1 and 2 require direct access to the server configuration.

What is that Application class lifecycle of a rest service?

Is every rest service starting with extending that application class and defining applicationpath? What is the lifecyce of that application class itself? Here is an example:
import javax.ws.rs.core.Application;
#javax.ws.rs.ApplicationPath("resources")
public class ApplicationConfig extends Application {}
Is this a servlet? Is it always alive? How shall I understand this class? Is it a cdi bean? Does the server creates this class on every request?
What is Application?
Application is a deployment agnostic abstract class provided by JAX-RS for configuring and registering the components of a JAX-RS application and it's also used to supply additional metadata to the application.
Application is one of the types that can be injected using the #Context annotation. For more details, refer to this answer.
Subclasses of Application
Application subclasses can implement methods such as getClasses(), getSingletons() and getProperties() for configuring and registering components and properties.
Application subclasses can be annotated with #ApplicationPath, defining the base URI for the JAX-RS resource classes (classes annotated with #Path). Application subclasses are instantied once when the web application starts and they are managed by the JAX-RS runtime.
The simplest implementation possible is as following:
#ApplicationPath("api")
public SampleApplication extends Application {
}
In the example above no resources classes or providers are registered, so the JAX-RS runtime will scan the classpath for JAX-RS components and will register them automatically.
However, according to this post from Jakub Podlesak, this approach is discouraged in production environments:
The above example works great. When started, the application just scans the actual class-path, and adds every single JAX-RS component class found there to the actual runtime configuration. Isn't is great? Frankly, this kind of configuration could work just fine. Until someone changes either the system configuration (system class-path) or the way how you application is being packaged (a new 3rd party component could be added/removed from the application class-path then). These changes could be out of your control and if one of them happens, you application configuration could break. For this reason, it is not wise to use this kind of configuration in a production environment.
Jersey, the JAX-RS reference implementation, provides the ResourceConfig class. Compared to Application, ResourceConfig provides advanced capabilities to simplify registration of JAX-RS components, such as scanning for root resource and provider classes in a provided classpath or a in a set of package names, etc. For more details, refer to the Jersey documentation.
Working with multiple Application subclasses
Is also worth mentioning that you are not restricted to a single Application subclass per web application. The same WAR can have multiple Application subclasses. For more details, have a look at this post from Adam Bien:
To deploy multiple JAX-RS applications with different URIs in one WAR you will have to create one javax.ws.rs.core.Application subclass per such an application (or use web.xml for this purpose). Obviously the in Java EE ubiquitous Convention over Configuration (or Configuration by Exception) cannot work any more: you will have to explicitly configure resources in each subclass by overriding the method getClasses or getSingletons:
#Path("first")
public class FirstResource {
#GET
public String first() {
return "first";
}
}
#ApplicationPath("one")
public class JAXRSConfigurationOne extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<>();
resources.add(FirstResource.class);
return resources;
}
}
#Path("second")
public class SecondResource {
#GET
public String first() {
return "second";
}
}
#ApplicationPath("two")
public class JAXRSConfigurationTwo extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<>();
resources.add(SecondResource.class);
return resources;
}
}
Both JAX-RS applications become accessible through distinct URIs: http://localhost:8080/multiple-roots/one/first and http://localhost:8080/multiple-roots/two/second
What if no Application subclass is present?
If no Application subclass is present, the JAX-RS implementations are required to add a servlet and set its name to javax.ws.rs.Application and to automatically discover all resource classes and providers which must be packaged with the application.
For further details, have a look at the chapter 2 of the JAX-RS 2.1 specification.

Why Initialization is not required in EJB?

I am new to the Struts2 framework and to EJB as well. I have a class LoginDAO which implements checkUser method of an interface LoginDAOLocal. I don't understand why I see different behavior for the following scenarios:
If I use an EJB (LoginDAO is stateless session bean) as follows, method call works perfectly without any error.
#EJB
private LoginDAOLocal loginDao;
loginDao.checkUser(userName,password);
If I use Struts2 as follows, it gives a Null pointer exception for the method call.
public class LoginAction extends ActionSupport {
// Getters setters for userName and password)
private LoginDAOLocal loginDao;
loginDao.checkUser(this.userName,this.password);
}
If I use a simple Java application (no EJB or Struts2), the method call creates a compile time error saying loginDao is not initialized
public static void main(String[] args) {
LoginDAOLocal loginDao;
loginDao.checkUser(userName,password);
}
Can someone explain why this different behavior ?
Without getting too much into the Java EE spec: EJBs are managed by an EJB container that exists in J2EE servers (JBoss \ Websphere etc..). The container takes control of bean lifecycle and is responsible for creating \ destroying beans according to the application needs.
When running out of container (simple java application) your beans won't get initialized and you don't have a JNDI context to get beans from, even if you add #EJB annotation to the field member.
We can say that there are two ways to manage the beans, using the container (managed by the container), or by another component (managed by a servlet, listener or filter).
Using components managed by the container, the container injects the references. e.g.:
#WebServlet("/test")
public class MyServlet extends HttpServlet {
#Resource(lookup = "jdbc/TestDS")
private DataSource testDS;
}
By contrast, a component managed by a bean, e.g.:
#Namespace("/User")
#ResultPath(value = "/")
#Result(name = "success", location = "pages/login.jsp")
public class LoginAction extends ActionSupport {
}
is managed by the filter org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter. The latter should be responsible for performing dependency injection. Spring, for example, takes care of injecting all necessary dependencies.

EJB injection not working

I have 2 EJB module projects and I want from one of the projects to call a stateless no-interface bean from another project. I want to inject the bean to be called using the EJB annotation. The problem is the injection doesn't work(I use NetBeans 7.4 if that is relevant).
The stateless no-interface EJB being called:
package standalonepackage;
import javax.ejb.Stateless;
import javax.ejb.LocalBean;
#Stateless
#LocalBean
public class StandaloneBean {
private static final String message="Greetings!";
public String returnMessage(){
return message;
}
}
The interface of the bean that calls the bean above(this ejb resides in another ejb module project)
#Local
public interface ExampleBeanLocal {
public String getMessage();
}
The implementation of the interface:
#Stateless
public class ExampleBean implements ExampleBeanLocal {
#EJB
private StandaloneBean standaloneBean;
#Override
public String getMessage() {
return String.format("Me - and the second message %s", standaloneBean.returnMessage());
}
}
I also have a main class that just calls the ExampleBean getMessage method(MainClass is located in the second ejb module project):
public class MainClass {
private static ExampleBeanLocal instance = new ExampleBean();
public static void main(String[] args) {
System.out.println(instance.getMessage());
}
}
What am I missing?
First of all if you want to access your business logic as EJB then first you will need to deploy the EJB in an application server. During the deployment process the application server will create something called the JNDI name which is like a gatepass to access your business logic.
Secondly, there are two ways you can invoke an EJB.
1. Creating ContextLookup using JNDI name
2. Using Context Dependency Injection CDI (only within the same Container)
You cannot invoke an EJB using CDI from a POJO ( since it is not contained in any container and the EJB your accessing is in a different JVM ). If you want to access an EJB from a POJO you'll need to use #Remote and use the ContextLookup way of accessing an EJB, you can find more information here
http://wiki.netbeans.org/CreatingEJB3UsingNetbeansAndGlassfish
You need application server with EJB container to run this. Have a look at JBoss, Apache TomEE or something else.
you can use this way to run your jar GLASFISH_HOME/bin/appclient -client app.jar
before compiling your maven project mvn assembly:assembly
and add your main class in your pom.xml

Categories

Resources