Is there a way I can get all packages related to my application in runtime? (e.g. list of strings)
I am interested only in packages that are declared in my application and not interested in those that come from dependencies.
I tried to search them by the main class:
System.getProperty("sun.java.command")
But, in this case, I can only get a package of my main class.
I assume it may differ of application and how it's packed. But, now I am mostly working with Spring Boot apps that are packed as jar.
You should try in this way, specify the main class package and do filter
spring boot main class package com.main
Package[] p = Package.getPackages();
for(Package p1 : p) {
if(p1.getName().startsWith("com.main"))
System.out.println(p1.getName());
}
Related
Currently I have quite a bit of services written in Spring Boot 2.1.3 (in mono-repo) and I have a common package that's used in most of the services.
So my packages are organized in the following:
root
root.common
root.serviceA
root.serviceA.<subpackages>
root.serviceB
root.serviceB.<subpackages>
...
root.serviceX
root.serviceX.<subpackages>
In each service I have Program.java where it's tagged with #SpringBootApplication (e.g. root/serviceA/Program.java)
The problem now I'm having is that I have a component in root.common (e.g. root/common/JSONSerializer.java) which needs to be loaded in all the services. I tried tagging the file with #JsonComponent but obviously it doesn't work because it's outside root.service* package.
I managed to get it working by manually adding #Import tag in the main file but that means that for each service I have to manually add #Import({root.common.JSONSerializer.class}) which is tedious and error prone. Is there a way to include this file in the component scanning process?
You can set each Program class by following way:
#SpringBootApplication(scanBasePackages = {"root.serviceA", "root.common"})
REFERENCES
SpringBootApplication documentation
I'm trying to extend webapp functionality without redeploying webapp archive. App runs under Glassfish 3.
Basically, what I did is the following:
webapp.war contains some part.jar which is a part of webapplication. It contains some class SomeClass. Webapp allows some custom configuration where descendant of SomeClass may be loaded dynamically using Class.forName.
I derived ExtensionClass from SomeClass (ExtensionClass extends SomeClass), compiled it using part.jar and got some extension.jar.
I tried to put extension.jar into domain/lib and domain/lib/ext. But then app when loading ExtensionClass says java.lang.NoClassDefFoundError: SomeClass (note that it says about parent class). It seems that classloader which loads library cannot find the base class, which is contained in the webapp.
Goal: I'd like to extend app using dependencies from it but without rebuilding.
Question: what can be done in this case?
Edit
If I just put my extension.jar into applications/mywebapp/WEB-INF/libs it obviously works, as if I'd put it into webapp.war itself. But it is very dirty, I want to solve it without doing this dirtiness and without touching webapp.war.
Hi, I have an EJB project called "service-ejb" with this:
#Stateless
#Remote(ServiceRemote.class)
public class Services implements ServiceLocal, ServiceRemote {
[...business code...]
}
the I have the local interface, in the same project:
#Local
public interface ServiceLocal { }
and the remote interface, in a class library project called "service-lib":
#Remote
public interface ServiceRemote {
public boolean checkIfOk();
}
I can deploy it without problem, alone or in a java EE application. The point is that I don't understand how to tell NetBeans that I wish to call that beans from another application. For example I have another java EE project with a war component, where inside a servlet I wrote:
#EJB
private ServiceRemote serviceTest;
but of course it will fail compiling, so I tried with:
InitialContext ic = new InitialContext();
ServiceRemote serviceTest = (ServiceRemote) ic.lookup("ServiceRemote");
with no luck... Where in NetBeans I can tell it to use the "service-lib" as a reference? I don't want it to be added as library and then deployed with the ear, I only want NetBeans to compile correctly the code.
Sorry if the question sound silly, but I've read the documentation and I don't understant what I'm missing...
--- edit ---
I'll try to be more clear. "service-ejb" reference "service-lib", so I've deployed "service-ejb" to glassfish. Correctly, I have:
glassfish_applications_directory $ find -name "service*"
./__internal/service-ejb
./__internal/service-ejb/service-ejb.jar
./service-ejb
./service-ejb/com/tecytal/components/email/beans/Service.class
./service-ejb/com/tecytal/components/email/interfaces/local/ServiceLocal.class
./service-ejb/service-lib.jar
Then I open a java EE project, let's call it "myEngine" with a war module "myEngine-war". I've tried to add to "myEngine-war" a reference to "service-lib", of course, and in this case it compile well. The point is that I can do everything, tell to netbeans NOT to package the "service-lib" with the war, but when I deploy "myEngine" I get:
glassfish_applications_directory $ find -name "service*"
./myEngine/lib/service-lib.jar
./__internal/service-ejb
./__internal/service-ejb/service-ejb.jar
./service-ejb
./service-ejb/com/tecytal/components/email/beans/Service.class
./service-ejb/com/tecytal/components/email/interfaces/local/ServiceLocal.class
./service-ejb/service-lib.jar
I DON'T want to have TWO service-lib.jar in my server, one in the service-ejb and one in myEngine, I don't understand how I can use a remote ebj in netbeans telling to him NOT TO deploy the same lib 3214899213 times :)
I recommend to add the remote service interface and all classes it references (including exceptions) in a separate jar. Then in the other 2 projects declare a dependcy to this jar.
I don't want it to be added as library and then deployed with the ear
I recommed to add only the service-lib contain the interface etc as a library and deploy it with the ear, not the whole service-ejb containing the implementation.
I have web application running with a default impl of a backend service. One should be able to implement the interface and drop the jar into the plugins folder (which is not in the apps classpath). Once the server is restarted, the idea is to load the new jar into the classloader, and have it take part in dependency injection. I am using Spring DI using #Autowired. The new plugin service impl will have #Primary annotation. So given two impls of the interface, the primary should be loaded.
I got the jar loaded into the classloader and can invoke the impl manually. But I haven't been able to get to to participate in the Dependency Injection, and have it replace the default impl.
Here's a simplified example:
#Controller
public class MyController {
#Autowired
Service service;
}
//default.jar
#Service
DefaultService implements Service {
public void print() {
System.out.println("printing DefaultService.print()");
}
}
//plugin.jar not in classpath yet
#Service
#Primary
MyNewService implements Service {
public void print() {
System.out.println("printing MyNewService.print()");
}
}
//For lack of better place, I loaded the plugin jar from the ContextListener
public class PluginContextLoaderListener extends org.springframework.web.context.ContextLoaderListener {
#Override
protected void customizeContext(ServletContext servletContext,
ConfigurableWebApplicationContext wac) {
System.out.println("Init Plugin");
PluginManager pluginManager = PluginManagerFactory.createPluginManager("plugins");
pluginManager.init();
//Prints the MyNewService.print() method
Service service = (Service) pluginManager.getService("service");
service.print();
}
}
<listener>
<listener-class>com.plugin.PluginContextLoaderListener</listener-class>
</listener>
Even after I have loaded the jar into the classloader, DefaultService is still being injected as service. Any idea how I get the plugin jar to participate into the spring's DI lifecycle?
Edited:
To put it simply, I have a war file that has a few plugin jars in a plugins directory inside the war. Based on a value from a configuration file that the app looks at, when the app is started, I want to load that particular plugin jar and run the application with it. That way, I can distribute the war to anyone, and they can choose which plugin to run based on a config value without having to to repackage everything. This is the problem I am trying to solve.
It seems like all You need is to create the Spring ApplicationContext properly. I think it's possible without classpath mingling. What matters most are the locations of the Spring configuration files within the classpath. So put all Your plugin jar's into WEB-INF/lib and read on.
Let's start with the core module. We'll make it to create it's ApplicationContext from files located at classpath*:META-INF/spring/*-corecontext.xml.
Now we'll make all plugins to have their config files elsewhere. I.e. 'myplugin1' will have its config location like this: classpath*:META-INF/spring/*-myplugin1context.xml. And anotherplugin will have the configs at classpath*:META-INF/spring/*-anotherplugincontext.xml.
What You see is a convension. You can also use subdirectiries if You like:
core: classpath*:META-INF/spring/core/*.xml
myplugin1: classpath*:META-INF/spring/myplugin1/*.xml
anotherplugin: classpath*:META-INF/spring/anotherplugin/*.xml
What matters is that the locations have to be disjoint.
All that remains is to pass the right locations to the ApplicationContext creator. For web applications the right place for this would be to extend the ContextLoaderListener and override the method customizeContext(ServletContext, ConfigurableWebApplicationContext).
All that remains is to read Your config file (its location can be passed as servlet init parameter). Than You need to construct the list of config locations:
String locationPrefix = "classpath*:META-INF/spring/";
String locationSiffix = "/*.xml";
List<String> configLocations = new ArrayList<String>();
configLocations.add(locationPrefix + "core" + locationSiffix);
List<String> pluginsTurnedOn = getPluginsTurnedOnFromConfiguration();
for (String pluginName : pluginsTurnedOn) {
configLocations.add(locationPrefix + pluginName + locationSiffix);
}
applicationContext.setConfigLocations(configLocations.toArray(new String[configLocations.size()]));
This way You can easily manage what is and what is not loaded into Spring ApplicationContext.
Update:
To make it work there's one more hidden assumption I made that I'm about to explain now. The base package of the core module and each plugin should also be disjoint. That is i.e.:
com.mycompany.myapp.core
com.mycompany.myapp.myplugin1
com.mycompany.myapp.anotherplugin
This way each module can use <context:componet-scan /> (on equivalent in JavaConfig) easily to add classpath scanning for it's own classes only. The core module should not contain any package scanning of any plugin packages. The plugins should extend configuration of ApplicationContext to add their own packages to classpath scanning.
If you restart the server, I see no reason why you can't just add the JAR to the WEB-INF/lib and have it in the CLASSPATH. All the complication of a custom class loader and context listener goes away, because you treat it just like any other class under Spring's control.
If you do it this way because you don't want to open or modify a WAR, why not put it in the server /lib directory? Let the server class loader pick it up. This makes all plugin classes available to all deployed apps.
The answer depends on how important the separate /plugin directory is. If it's key to the solution, and you can't add the JAR to the server's /lib directory, then that's that. I've got nothing. But I think it'd be worthwhile to at least revisit the solution you have to make sure that it's the only way to accomplish what you want.
I have two war file app1.war and app2.war deployed in a single JBoss instance. Package names for java classes for both war files starts with com.myapp
To add further, there are some Classes that are common between the two apps while there are some that have same fully qualified class names but are different (Source Code has changed).
I want to know, if this could pose threat of any kind to the deployment scenario?
You could get class loading problems if your applications are not isolated, i.e. have their own class loading repository and class loaders. If you configure JBoss to isolate the applications from each other you should be fine (I don't know what is the default for your version but 4.2.3 that we use does not isolate apps by default).
To clarify that a bit:
If you have two classes with different implementations but the same FQCN you could get the wrong class from the class loader for the application that is loaded second. Even if the implementation was the same you could get class cast exceptions or other strange behavior if one app gets the class from the other app.
I had a similar situation with multiple apps.Look at my solution here
Best way is to isolate class loading for your application archives.
For JBoss 5.1.0 GA following worked for me.
Create jboss-classloading.xml file in WEB-INF folder.
Added following lines to this file
Here,
export-all="NON_EMPTY" => Makes sure the classes loaded for this app is not exported
import-all="true" => Imports and uses all of the class definition available.
parent-first="false" => If more than one class with same name is found, one defined under the application will be used first.
FYI. This also helped me embedding the log configuration of log4j in the application war file. Will need to place log4j.xml in WEB-INF/classes and have a log4j.jar in WEB-INF/lib folder.
There will be one class loader instance for each application or standalone module. In other words, classes in app1.war will be loaded in different class loader than the classes in app2.war. This is the default behavior of any Java EE server; So it really doesn't matter about having classes with the same package/names and/or different content. This is the default behavior of any Java EE server.
Having said that, if you tweak the class loader policy of the server or try to load classes (reflect) using anything other than Thread.currentThread().getContextClassLoader(), you could be asking for trouble.