How to make Java serviceLoader of a external library work - java

I'm working on a plugin for a 3rd party software that is quite undocumented. For the plugin I'm using a external lib (.jar) managed by maven and is later on executed on a tomcat server. Everything was working great till I updated to the latest release of the library, which now is using java.util.serviceLoader in order to load a concrete implementation of a interface. When build, my project has this package structure on a tomcat server:
mypluginPackage.jar
|
---META-INF
---lib
|
--- theExternalLibUsingServiceLoader.jar
|
---META-INF
|
---services
|
--- full.path.to.TheFactoryInterface
---external.lib.path
|
--- TheFactoryInterface.class
--- TheConreteClass.class
--- mypluginCore.jar
|
---META-INF
--- my.plugin.path
|
--- MyClassUsingTheExternalLib.class
As you can see, the external library has the correct services entry which is necessary for the serviceLoader to find the concrete of a interface within META-INF. The content of the full.path.to.TheFactoryInterface file is full.path.to.TheConcreteClass, which seems legit.
My plugin (package as well as the core) however do not have any of those META-INF information.
What happens now is that every time my plugin is using a method that will trigger the serviceLoader of the external library, the serviceLoader is unable to find the concrete implementation.
What I already tried is adding the exact same services/full.path.to.TheFactoryInterface to all my META-INF directories, which is not working (I guess I would need to change the content of the full.path.to.TheFactoryInterface file but I'm not sure - because of the undocumented plugin structure for the 3rd party software - what the right (relative) path would look like).
Can anybody give me a shot what I'm doing wrong here and how to fix it so the concrete class is found by serviceLoader? What META-INF folder should the services folder with it's content be placed in and should I change the paths? Is that the error at all or am I missing something compleatly different?
I understand that this is a very special topic since the 3rd party software is unkown, but any information depending serviceLoader and it's behaviour when run across multiple jars with multiple META-INF folders and in different execution context / classpaths would be appreciated.

I was able to get it working by extending the external lib so I'm able to provide a custom ClassLoader. Turned out, the default ClassLoader was wrong.
Also I was able to get in touch with the 3rd party tool devs and get the information I needed - call it "social decompiling".
Everything working now.

Related

PersistenceException: No Persistence provider for EntityManager named

I have written (with Eclipse) a small program which uses JPA. Now I want to manualy compile and run this program using command line. I have also placed all source files in a separate directory (not within Eclipse's work space). The directory looks like:
directory
|- src
| |- javax
| | |- persistence
| | |- <...>
| |- main
| |- META-INF
| | |- javax
| | | |- persistence
| | |- persistence.xml
| |
| |- <..>
|- run.bat
Note: I purposely have 2 javax directories with all the class files in them.
run.bat contains lines:
javac -cp ./src ./src/main/Main.java
java -cp ./src main.Main
As far as I understand I am getting errors not because of the issue with the META-INF/persistence.xml file (not like many other people on stackoverflow). I am saying so because I have writen following lines in my Main:
<...>
Thread.currentThread().setContextClassLoader(new ClassLoader() {
#Override
public Enumeration<URL> getResources(String name) throws IOException {
System.out.println("resource requested=" + name);
if (name.equals("META-INF/services/javax.persistence.spi.PersistenceProvider")) {
<--- MARK ---> return Collections.enumeration(Arrays.asList(new File("src/javax/persistence/spi/PersistenceProvider.class")
.toURI().toURL()));
} else if (name.equals("META-INF/persistence.xml")) {
return Collections.enumeration(Arrays.asList(new File("src/META-INF/persistence.xml")
.toURI().toURL()));
}
return super.getResources(name);
}
});
emFactory = Persistence.createEntityManagerFactory("uzduotis");
<...>
So when I launch run.bat the program prints:
resource name=name=META-INF/services/javax.persistence.spi.PersistenceProvider
(it never gets to the part where program asks for "META-INF/persistence.xml" resource)
then it breaks and gives me an error:
If I change MARKED statement to
return super.getResources("src/" + name);
then the error says only:
What am I missing?
Peristence.xml:
EDIT:
Program prety simple: no hibernate, just a few classes which retrieve some info from REST service and work with it, mainly using javax and w3c external libraries. Photo of the project explorer to depict its simplicity.
EDIT 2. SOLUTION
I found a way to make it work although I am not entirely sure about the causes. So if anyone could answer why the following changes solved the issue I would gladly mark it as an answer
I added eclipselink and mysql-connector-java jars to the src directory. eclipselink.jar fixed the issue and mysql-connector-java.jar was needed because I got other error (I am working with database afterall). Finally I changed line in the run.bat file from
java -cp ./src main.Main
to
java -cp ./src;./src/eclipselink.jar;./src/mysql-connector-java.jar; main.Main
It seems that your application is a standard Java SE application. JPA does not work automatically, as standard Java SE environment does not contain any implementation of JPA interfaces. It seems that Eclipse adds eclipselink to the classpath under the cover, therefore your program works when you run it from within Eclipse. But when you run your program from command line, you need to add eclipselink, which is a library that contains functionality for JPA interfaces.
You may try to generate Ant build file from eclipse project, and you would see if it adds eclipselink into the classpath in build.xml file. Or even better, I recommend to turn your project into project with Maven build - you would be able to build and run your project using Maven command in the same way as Eclipse would execute it (as Eclipse uses Maven under the cover for Maven-based projects).
Edit - explanation of the role of eclipselink in JPA
Packages starting javax.persistence are mostly interfaces, annotations, they have very few classes in fact. They only define how your application communicates with underlying persistence mechanism in standard way. On the other hand, you need to include also library with implementations (classes) for the standard JPA interfaces. javax.persistence.Persistence is just a facade, which delegates all the hard work onto an implementation, which might be Eclipselink, Hibernate, or something else.
The relationship between javax.persistence and EclipseLink is similar to relationship between interfaces and classes. You may use interfaces in your code, but your application will work only if you supply interfaces with real classes at some point.
It is also possible to use Eclipselink directly and skip whole javax.persistence layer, similarly as it is possible to work with real classes directly without through interfaces. But then you need learn internal API of Eclipselink and it is not possible to change Eclipselink library for alternatives like Hibernate. Nor will you be able to reuse the knowledge of Eclipselink-specific API to a project, which uses Hibernate. Reusability of a standard API is the most important case for standardized javax.persistence interfaces.
Maybe you wonder, how the persistence API knows where it should delegate the required functionality. There are multiple hooks, how it can find out. It is possible to include path to the main implementation class in persistence.xml using provider element. Or, it simply works if you include Eclipselink into the classpath, as Eclipselink library contains special text file at standardized path, which contains required information. This special file is inside Eclipselink JAR at path META-INF/services/javax.persistence.spi.PersistenceProvider - does it ring the bell?

Globs in sonar-project.properties

I'm working on a large legacy project that I'm trying to componentize, starting with SonarQube. I'm configuring a multi-module project in sonar-project.properties. This works fine. However, I have an issue precisely identifying source folders.
Unfortunately, our modules aren't neatly separated in the file system. The project is separated into many Eclipse projects, and several Eclipse projects together form one module. I can, of course, enumerate all the projects, but this is very cumbersome as there are a lot of them. Here's a (simplified) version of our directory structure:
projects/
moduleAsubmodule1/
src/
com/mycompany/moduleA/submodule1/
moduleAsubmodule2/
src/
com/mycompany/moduleA/submodule2/
moduleBsubmodule1/
src/
com/mycompany/moduleB/submodule1/
moduleBsubmodule2/
src/
com/mycompany/moduleB/submodule2/
moduleBsubmodule3/
src/
com/mycompany/moduleB/submodule3/
Imagine many more modules and submodules, where the project name is concatenated, but the package names are nicely divided, making it much easier to differentiate on those.
moduleA.sonar.projectBaseDir=.
moduleA.sonar.sources=projects/**/src/com/mycompany/moduleA/**/*
moduleA.sonar.test=projects/**/*.test/src/com/mycompany/moduleA/**/*
According to the documentation, this should be possible for exclusions. However, I get the following error message:
16:10:44 ERROR: Unable to execute Sonar
16:10:44 ERROR: Caused by: The folder 'projects/**/src/com/mycompany/mymodule/**/*' does not exist for 'XXX:XXX:mymodule' (base directory = D:\XxxSonar\.)
So I guess globs don't work for sources? If that's indeed the case, what can I do?
We use SonarQube 4.1.2.
I had the same issue, but I solved it by doing like this:
sonar.sources=.
sonar.inclusions=projects/*/src/**/*
The inclusions/exclusions properties support wildcards. Same for your tests:
sonar.test.inclusions=projects/*/*.test/src/**/*
Wildcards are not allowed when specifying "sonar.sources". Instead, you should play with the properties that allow to narrow your source and test files. See the documentation page on how to include or exclude files to narrow the focus.

Issues with obfuscation in Java using ZKM

Im trying to obfuscate an eclipse based java application. We are using ZKM script for Obfuscation.we recently added a new feature to the existing application where we have used the poi-3.9-20121203.jar third party jar and there are around 3 to 4 dependent jars for this. Im getting the below error when trying to obfuscate the code
Unexpected Error (D). Please report the problem to bugs#zelix.com
com.zelix.m7: Class 'org.apache.poi.ss.usermodel.Sheet' in file 'D:\workSpace\Test Ofucscation\Relea
se3\mit\plugins\com.ins.hi.stores_1.0.0.201404081434.jar!lib/poi-3.9- 20121203.jar!org/a
pache/poi/ss/usermodel/Sheet.class' has been opened and is implemented by class 'org.apache.poi.xssf
.usermodel.XSSFSheet' in file 'D:\workSpace\TestOfucscation\Release3\mit\plugins\poi-ooxml-3.9-2
0121203.jar!org/apache/poi/xssf/usermodel/XSSFSheet.class'. Class 'org.apache.poi.xssf.usermodel.XSS
FSheet' is used by the opened classes but it has not been opened. Either 'org.apache.poi.ss.usermod
el.Sheet' should not be opened or 'org.apache.poi.xssf.usermodel.XSSFSheet' must also be opened for
obfuscation. (B)
at com.zelix.w1.a(w1.java:361)
at com.zelix.w1.b(w1.java:95)
at com.zelix.rp.a(rp.java:7)
at com.zelix.rp.c(rp.java:113)
at com.zelix.rp.a(rp.java:101)
at com.zelix.q0.a(q0.java:128)
at com.zelix.q0.a(q0.java:160)
at com.zelix.zo.a(zo.java:463)
at com.zelix.zo.a(zo.java:710)
at com.zelix.zo.<init>(zo.java:351)
at com.zelix.f3.a(f3.java:1130)
at com.zelix.f3.a(f3.java:1249)
at com.zelix.i2.a(i2.java:15)
at com.zelix.q3.a(q3.java:222)
at com.zelix.x1.a(x1.java:1490)
at com.zelix.t1.a(t1.java:1793)
at com.zelix.t1.a(t1.java:39)
at com.zelix.oc.a(oc.java:96)
at com.zelix.nc.a(nc.java:89)
at com.zelix.mc.a(mc.java:22)
at com.zelix.ic.a(ic.java:5)
at com.zelix.ge.a(ge.java:3)
at com.zelix.ak.a(ak.java:291)
at com.zelix.ak.<init>(ak.java:438)
Below are the things that I have done
After including the new feature and third party jar in the application, I have updated the classpath of the third party jar& all the dependent jars under the classpath in the Obfuscation_Script.txt.
before executing the obfuscation script, i have replaced the poi-3.9.jar with only the classes that Im using in the classes that I have newly developed.
But im getting the above error when I run the obfuscation script.
Any help on this is much appreciated. Thanks in Advance.
In ZKM script, you have to include the dependent jar files for poi-3.9.jar.
classpath ".\lib\poi-3.9-FINAL\*.jar"

Liferay: Any method written in *LocalServiceImpl not found in *LocalServiceUtil

Any method I write in *LocalServiceImpl is not found in *LocalServiceUtil. I re-run service builder every time and nothing. The methods are recognized on local server but not found in production server. All old methods are working, just new written methods are not found. What could be the problem? Thanks. I am using Liferay 6.1.1 GA2 on both servers.
this is my service builder log
[echo] Loading jar:file:/C:/sbl-workspace-1/liferay-portal-6.1.1-ce-ga2/tomcat-7.0.27/webapps/ROOT/WEB-INF/lib/portal-impl.jar!/system.properties
[echo] 28.11.2013 08:30:46 com.liferay.portal.kernel.log.Jdk14LogImpl info
[echo] INFO: Global shared lib directory /C:/sbl-workspace-1-plugins-sdk/liferay-plugins-sdk-6.1.1/lib/
[echo] 28.11.2013 08:30:46 com.liferay.portal.kernel.log.Jdk14LogImpl info
[echo] INFO: Global lib directory /C:/sbl-workspace-1/liferay-portal-6.1.1-ce-ga2/tomcat-7.0.27/lib/ext/
[echo] 28.11.2013 08:30:46 com.liferay.portal.kernel.log.Jdk14LogImpl info
[echo] INFO: Portal lib directory /C:/sbl-workspace-1/liferay-portal-6.1.1-ce-ga2/tomcat-7.0.27/webapps/ROOT/WEB-INF/lib/
[echo] 28.11.2013 08:30:47 com.liferay.portal.kernel.log.Jdk14LogImpl info
[echo] INFO: Properties for portal loaded from [file:/C:/sbl-workspace-1/liferay-portal-6.1.1-ce-ga2/tomcat-7.0.27/webapps/ROOT/WEB-INF/classes/portal-ext.properties, jar:file:/C:/sbl-workspace-1/liferay-portal-6.1.1-ce-ga2/tomcat-7.0.27/webapps/ROOT/WEB-INF/lib/portal-impl.jar!/com/liferay/portal/tools/dependencies/portal-tools.properties, jar:file:/C:/sbl-workspace-1/liferay-portal-6.1.1-ce-ga2/tomcat-7.0.27/webapps/ROOT/WEB-INF/lib/portal-impl.jar!/portal.properties]
[echo] Loading jar:file:/C:/sbl-workspace-1/liferay-portal-6.1.1-ce-ga2/tomcat-7.0.27/webapps/ROOT/WEB-INF/lib/portal-impl.jar!/portal.properties
[echo] Loading jar:file:/C:/sbl-workspace-1/liferay-portal-6.1.1-ce-ga2/tomcat-7.0.27/webapps/ROOT/WEB-INF/lib/portal-impl.jar!/com/liferay/portal/tools/dependencies/portal-tools.properties
[echo] Loading file:/C:/sbl-workspace-1/liferay-portal-6.1.1-ce-ga2/tomcat-7.0.27/webapps/ROOT/WEB-INF/classes/portal-ext.properties
[echo] Building Menus
[echo] Building MenusImages
[echo] Building Sections
[mkdir] Created dir: C:\sbl-workspace-1-plugins-sdk\liferay-plugins-sdk-6.1.1\portlets\AdminSBL-portlet\docroot\WEB-INF\service-classes
compile-java:
[copy] Copied 65 empty directories to 65 empty directories under C:\sbl-workspace-1-plugins-sdk\liferay-plugins-sdk-6.1.1\portlets\AdminSBL-portlet\docroot\WEB-INF\service-classes
[javac] Compiling 398 source files to C:\sbl-workspace-1-plugins-sdk\liferay-plugins-sdk-6.1.1\portlets\AdminSBL-portlet\docroot\WEB-INF\service-classes
[zip] Building zip: C:\sbl-workspace-1-plugins-sdk\liferay-plugins-sdk-6.1.1\portlets\AdminSBL-portlet\docroot\WEB-INF\lib\AdminSBL-portlet-service.jar
[delete] Deleting directory C:\sbl-workspace-1-plugins-sdk\liferay-plugins-sdk-6.1.1\portlets\AdminSBL-portlet\docroot\WEB-INF\service-classes
BUILD SUCCESSFUL
Total time: 37 seconds
SOLVED:It worked like yannicular and Pankaj Kathiriya said. Thanks.....
Most probably, your service-build is failing. You can still see the old methods, because you are still using the old .jar, the last one that was successfully built. My advice is to check the build-service log and find the error that's failing the service-build task
Edit: Sometimes, conflicts from the Portal Deployment mechanism, or dynamic Class loading could lead to load Classes from an old service jar. If you are sure that your webapp is legit but the deployment fails, a fail-safe procedure to make sure you are deploying your application is:
Shut down the Portal
Delete the app folder from portal webapps.
Delete the app folder from work/Catalina/localhost
Copy your fresh webapp into /deploy and start the portal.
Bonus hint: If you are deploying into a running remote Portal, it's nice to copy your .war into the remote system, and then move it to the /deploy Folder. Else, if your connection is slow the deployment could start before the .war transfer is complete, the delpoyment fails and your app stays black-listed until you restart your tomcat
Could you Check the visibility of your methods: are they public ?
Service builder only creates *LocalServiceUtil methods for the public methods of *LocalServiceImpl.
It would be useful to see the your *LocalServiceImpl method to try to understand what is happening.
Well you should delete the service.jar which gets created when you build service.
Now again build the service.xml and check your methods will be populated under util class.
Try putting that .jar file under your Tomcat_home\lib\ext if it is being shared by multiple portlets.
This is an old problem but still doesn't have an accepted answer. As this question mentioned this old problem it came to my attention. In case somebody else stumbles upon it, here's a late guess on what might have gone wrong (from my answer to the other question):
First of all: Check the sourcecode for your *LocalServiceUtil and make sure it really doesn't have the method's implementation. If the method is there, you'll have to look elsewhere. Here's where:
When you deploy a portlet, you can get the API on multiple ways: Someone might have deployed the plugin containing the services on tomcat's global class path. If you don't update that one, you probably won't see your updates.
Also, someone might have copied an older version of your plugin into their own plugin as well - naturally not seeing any updates unless they update their dependency. Check for changed names of the jar file as well. Also check for the classes: Some people like to add the compiled java classes to their source directories, ending up with two different implementations of the same code.

Grails 2.1.1 - How to develop a plugin with an AstTransformer?

I want to replace the auto injected log object, which is of type org.apache.commons.logging.Log with an object of type org.slf4j.Logger, so that I can use it properly with Logback.
Therefore I need to create a ...Transformer class (written in Java) - that's what I got from Graeme Rocher over at the "grails-user" mailing list. I'm also aware that I have to pack this ...Transformer class within a plugin and make it a *.jar archive which I can load within the lib/ folder of the plugin. But I guess I'm doing something wrong here as I have the class, along with a META-INF folder which contains the MANIFEST.MF file as well as another folder services which holds the following file org.codehaus.groovy.transform.ASTTransformation which holds just one String: the canonical name of the ...Transformer class.
Now, if I try to do a grails clean everything is fine, BUT if I try to run grails package-plugin the console comes up with a java.lang.ClassNotFoundException.
Clipping from Stacktrace:
| Packaging Grails application...
| Error Fatal error during compilation org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Could not instantiate global transform class my.package.ast.LoggingTransformation specified at jar:file:/C:/Source/MyGrailsAST/lib/replace-logging-logback-ast.jar!/META-INF/services/org.codehaus.groovy.transform.ASTTransformation because of exception java.lang.ClassNotFoundException: my.package.ast.LoggingTransformation
1 error
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Could not instantiate global transform class my.package.ast.LoggingTransformation specified at jar:file:/C:/Source/MyGrailsAST/lib/replace-logging-logback-ast.jar!/META-INF/services/org.codehaus.groovy.transform.ASTTransformation because of exception java.lang.ClassNotFoundException: my.package.ast.LoggingTransformation
Does anybody have some experience with Grails plugins which handle with AstTransformer and could give me some advice on this? Is there a good tutorial out there which I haven't seen so far?
Please let me know ;)
so, after some research, browsing and finally asking at the Grails Mailing List (see the mailing list archives at: http://grails.1312388.n4.nabble.com/Grails-user-f1312389.html I found a solution.
my goal was to create a Globals ASTTransformation, to inject a org.slf4j.Logger object instead of the usual org.apache.commons.logging.Log object into every Artefact class without annotation.
so, here are the steps:
I created Java class similar to https://github.com/grails/grails-core/blob/master/grails-logging/src/main/groovy/org/codehaus/groovy/grails/compiler/logging/LoggingTransformer.java but with my own implementation of the org.slf4j.Logger object. It is crucial that you place the Java.class under the following package: org.codehaus.groovy.grails.compiler as
Grails scans for classes that are annotated with #AstTransformer in this package. (Graeme Rocher)
and pack it into a JAR along with its MANIFEST.MF file within the META-INF/ folder. A META-INF/services directory with all its stuff is not needed as Graeme Rocher stated:
You do not need the META-INF/services stuff and I would remove it as it is probably complicating matters.
So, I guess this statement was more related to my specific problem as I only have one #AstTransformer class within my plugin, but that's just a guess. And I haven't searched for further informations on this topic. Maybe some other developer here who needs this could do some research and share his solution within this thread...
The JAR should be imported to the plugin and placed under the lib/ directory. After this you should be able to do grails clean, grails compile and grails package-plugin.
If you want to replace the log implementation, as I did, you should exclude the grails-logging and grails-plugin-log4j JARs from your designated project's classpath. This is done in the BuildConfig.groovy file:
inherits("global") {
excludes "grails-plugin-log4j", "grails-logging"
}
Now install your plugin grails install-plugin \path\to\plugin.zip and everthing should work as expected.
Hope this helps...

Categories

Resources