ServiceLoader and META-INF/services generator between different modules in maven? - java

I would like to use service ServiceLoader between diferrent modules in a maven Project. I have a parent module called iMage. In the parent module there is a module with a name jmjrst.main and it has a public abstract class called JmjrstPlugin.
Then there is another module called prizm-plugin with the following class:
public class HelloWorldPlugin extends JmjrstPlugin{ ... }
I added jmjrst.main as a dependency to prizm-plugin and vica-versa as well.
In order to use ServiceLoader I wanted to use META-INF/services generator.
I added the following line to the pom.xml of prizm-plugin:
<dependency>
<groupId>org.kohsuke.metainf-services</groupId>
<artifactId>metainf-services</artifactId>
<version>1.1</version>
<optional>true</optional>
</dependency>
And the class HelloWorldPlugin starts like that:
#MetaInfServices(JmjrstPlugin.class)
public class HelloWorldPlugin extends JmjrstPlugin{ ... }
On the website on META-INF/services generator goes: "When you use javac in JavaSE6, META-INF/services/* files are generated automatically. No additional compiler switches are necessary. This library handles incremental compilation correctly, too."
At my case nothing is generated. Can somebody help me with that?

From the code snippet of your class HelloWorldPlugin it is not clear which interface is 'the contract'. And in the link that you gave:
If you have multiple interfaces and/or base type, the library cannot infer the contract type. In such a case, specify the contract type explicitly by giving it to #MetaInfServices ..
So first of all you have to be sure which contract you wish to fulfill and if the parent class(es) implement several then you'll need to explicitly state which one in the #MetaInfServices annotation.
That's the first thing to check I think.

Related

Add an annotation to a class from a place other than class definition

I need to add an annotation to a class which is in a sperate dependency (in a separate jar) of the project that I am working on. I am wondering that can I do this in Java?
As an example, I need to add an annotation HisClass without touching the source code of HisClass.
public class HisClass{
// ...
}
So, that the above class should be look like below at the build time,
#MyAnnot ( me = MyClass.class )
public class HisClass{
// ...
}
There are many ways:
Write a compiler plugin for javac, and make it add the annotations. This will be quite difficult, as the plugin API has nearly no documentation.
2.(Maybe not possible) Inject the annotation after compiling. Add an extra buildstep after compiling and use a library like ASM to add this annotation to the classfile.

How to use the spring-cloud-function-deployer with POJOs in Function signatures

I have three maven projects:
demo-api: Contains POJOs for function APIs i.e. OutputPojo.class, InputPojo.class
demo-packaged-function: Defines a single function public OutputPojo apply(InputPojo) {...} that depends on the 'demo-api' project.
demo-deployer: Uses spring-cloud-function-deployer to run the packaged function, also depends on the 'demo-api' project.
If I use simple types for the function signature (i.e. refactor to String apply(String input) {...} then everything works fine. However, with the setup described above I get the following exception:
Exception in thread "main" java.lang.ClassCastException: class org.example.function.api.InputPojo cannot be cast to class org.example.function.api.InputPojo (org.example.function.api.InputPojo is in unnamed module of loader 'app'; org.example.function.api.InputPojo is in unnamed module of loader org.springframework.cloud.function.deployer.FunctionArchiveDeployer$1 #7fedfe27)
Which makes sense becaues the class InputPojo is loaded by both class loaders. If I don't package the function as a fat jar, i get ClassNotFoundException when trying to deploy the jar, and I can't remove the dependency on demo-api from the deployer project as otherwise I cannot use the POJO classes when calling the function. How is this meant to work without simple argument types for the functions?
The documentation doesn't cover this, and while there are examples of packaged functions using POJOs, i cannot find any examples of them actually being called by the deployer - other than one is the unit tests which demos type conversion and converts the POJO to Message.

Java : Interface in a class is not accessible while importing

I am using the maven dependency hive-hcatalog-core in my program
and this jar present in the project maven dependencies, with the interface (as in the image-top).
The interface ICacheableMetaStoreClient , though present the class, is NOT resolvable from import org.apache.hive.hcatalog.common. (image -bottom)
consequently, while doing a spark-submit, I am getting the exception :
com.google.common.util.concurrent.UncheckedExecutionException:
java.lang.IllegalArgumentException: interface
org.apache.hive.hcatalog.common.HiveClientCache$ICacheableMetaStoreClient
is not visible from class loader
What do I need to do for this to be visible from the program class path.
Lets look at the code:
class HiveClientCache {..}
The HiveClientCache has only package level visibility and it along with ICacheableMetaStoreClientwont wont be available for import outside of that package (this includes in your code).
Now lets look at ICacheableMetaStoreClient:
#InterfaceAudience.Private
public interface ICacheableMetaStoreClient extends IMetaStoreClient {....}
The interface is public but has annotation on it that makes the Hive/Hadoop additional preprocessing to check object type and throw IllegalArgumentException.
Here is the JavaDoc for InterfaceAudience:
Annotation to inform users of a package, class or method's intended
audience. Currently the audience can be InterfaceAudience.Public,
InterfaceAudience.LimitedPrivate or InterfaceAudience.Private. All
public classes must have InterfaceAudience annotation.
Public classes that are not marked with this annotation must be considered by default as InterfaceAudience.Private.
External applications must only use classes that are marked InterfaceAudience.Public. Avoid using non public classes as these
classes could be removed or change in incompatible ways.
Hadoop projects must only use classes that are marked InterfaceAudience.LimitedPrivate or InterfaceAudience.Public
Methods may have a different annotation that it is more restrictive compared to the audience classification of the class. Example: A class
might be InterfaceAudience.Public, but a method may be
InterfaceAudience.LimitedPrivate

Injector Hierarchy / Child Injector explanation?

I have been looking over the net for an explanation about the injector heirarchy and how/when to use createChildInjector(), but I cannot find a clear and concise explanation.
Here is my use case:
I have a base application module which I use to inject certain context items. This module should be included in every injector instance.
I have a search module which searches a database
I have a search module which searches ElasticSearch. Some of the bindings in this class should override the bindings that are provided in the database search module.
For example, say the database search module contains:
bind(PlaceSearch.class).to(HibernatePlaceSearch.class);
bind(PersonSearch.class).to(HibernatePersonSearch.class);
And the ElasticSearch module contains:
bind(PersonSearch.class).to(PersonElasticSearch.class);
Is there a way to create an injector that includes the PlaceSearch binding from the database search module and the PersonSearch binding from the ElasticSearch module without creating a separate module that contains
bind(PlaceSearch.class).to(HibernatePlaceSearch.class);
bind(PersonSearch.class).to(PersonElasticSearch.class);
? Is this a case for Modules.override()? A case for createChildInjector? Thanks ahead of time!
The Modules.override() is not working in Stage.PRODUCTION. You should use PrivateModule where the binding is valid/visible only inside private module, so you can bind different implementation classes to the same interface. Then you can install the Private module to the parent Module, but you have to explicitly expose() all binding you want to make visible for other modules.
Guice - Private Modules
Lets say:
DatabaseSearchModule.java (extends PrivateModule)
bind(PlaceSearch.class).annotatedWith(Names.named("dbSearch")).to(HibernatePlaceSearch.class);
bind(PersonSearch.class).to(HibernatePersonSearch.class);
expose(PlaceSearch.class).annotatedWith(Names.named("dbSearch"));
EleasticSearchModule.java (extends PrivateModule)
bind(PersonSearch.class).annotatedWith(Names.named("elastic")).to(PersonElasticSearch.class);
expose(PersonSearch.class).annotatedWith(Names.named("elastic"));
Well then you can install it in some Parent abstract or servlet module
MainModule.java
install(new DatabaseSearchModule());
install(new EleasticSearchModule());
bind(OtherClass.class);
OtherClass.java
#Inject #Named("elastic")
private PlaceSearch elasticSearch;
#Inject #Named("dbSearch")
private PlaceSearch dbSearch;
You can use Named annotation or you can create very elegant own binding Annotation.
This is a perfect case for Modules.override().
Most applications shouldn't use child injectors. They add a lot of configuration complexity and have some surprising behavior for corner cases.

Maven use resources from other modules?

how can I use resources from other maven modules? My goal is to provide a AbstractImportClass as well as the to be imported files in a specific maven module. And use this module within other modules extending this class.
Let's say ModuleA contains src/main/java/MyAbstractImportClass.java, and src/main/resources/MyImport.csv
I now want to use the abstract import class in ModuleB. Or rather, I will extend it, use the abstract-fileimport, and a few custom functions.
Then ModuleC also uses the abstracts' import and some custom functions.
The problem is: the import in abstract class goes with reader and InputStream. When I execute just ModuleA everything is fine.
But when I tried to include the module via maven pom, and then extend the module to call the import, then I get NullPointerException at the line where the reader is used.
So obvious I cannot use foreign module resources this way.
But how could I instead make use of this?
Update:
Module A:
src/main/java/path/to/MyClassA.java
src/main/resources/path/to/test.txt
abstract class MyClassA {
public static String TESTFILE = test.txt;
List<String> doImport(String filename) {
InputStream fileStream = resourceClass.getResourceAsStream(filename);
//some precessing
return list;
}
}
Module B:
src/main/java/path/to/MyClassB.java
class MyClassB implements MyClassA {
List<String> list = doImport(TESTFILE);
}
If I put MyClassB in same dir as A, then everything works fine.
If I build B in a own module I get NullPointer for InputStream, what means the file is not found.
I don't think your problem is related to Maven at all. Class.getResourceAsStream() resolves relative paths as relative to the class object that you call it on. Therefore, if you use that method in an abstract class, every subclass of it could be looking for the resource in a different place.
For example, given three classes:
Super:
package com.foo;
public class Super {
{ System.out.println(getClass().getResourceAsStream("test.properties")); }
}
Sub1, a subclass of Super:
package com.foo.bar;
import com.foo.Super;
public class Sub1 extends Super {}
Sub2, another subclass:
package com.foo.bar.baz;
import com.foo.Super;
public class Sub2 extends Super {}
If you create a Super, it'll look for the classpath resource "/com/foo/test.properties" because that's how the path "test.properties" resolves relative to the class com.foo.Super. If you create a Sub1, it'll look instead in "/com/foo/bar/test.properties", and for a Sub2 instance, it'll look in "/com/foo/bar/baz/test.properties".
You might want to use an absolute path to the resource instead of a relative one, or else have the subclasses specify paths relative to themselves. It depends on your design and what kind of abstraction you're trying to achieve.
It's not exactly clear what your code does. Could you provide sample of how you're reading resource? If you do it properly - by getting InputStream from resource file in classpath there should be no problem. You can start by checking that ModuleA.jar has your resource file inside.
You should check:
Module B depend on Module A in pom.xml
The passed in 'filename' parameter starts with a '/', that is to say, the 'filename' parameter is '/path/to/test.txt' other than 'path/to/test.txt'
You program should work if these two conditions is satisfield.

Categories

Resources