Using Java classpath to execute Java 9+ Services - java

I have created a simple Java9 service example with one service interface and two service implementations and one driver which uses the ServiceLoader. I was able to successfully execute the example with module-path but when I tried to execute the same with java -cp (classpath) I did not get any output, not even any error.
Directory structure
out
├── driver
│   ├── com
│   │   └── company
│   │   └── driver
│   │   └── driver.class
│   └── module-info.class
├── firstServiceImpl
│   ├── com
│   │   └── company
│   │   └── first
│   │   └── serviceImpl
│   │   └── FunImpl1.class
│   └── module-info.class
├── secondServiceImpl
│   ├── com
│   │   └── company
│   │   └── second
│   │   └── serviceImpl
│   │   └── FunImpl2.class
│   └── module-info.class
└── serviceInterface
├── com
│   └── company
│   └── service
│   └── Fun.class
└── module-info.class
command to compile
javac -d out --module-source-path src src/driver/driver.java src/firstServiceImpl/FunImpl1.java src/secondServiceImpl/FunImpl2.java src/serviceInterface/Fun.java
The above command compiles the code with a module-source-path.
command to run
java -cp out/driver:out/serviceInterface/:out/firstServiceImpl/ com.sunil.driver.driver
The above command runs the code with a classpath.
After compiling with module-source-path and run the code with classpath. I do not get any error or output.
Please help me understand why there is no output when run using classpath.

The way service-providers are located depends on if the service-providers are on the classpath or the modulepath. This is described in the documentation of ServiceLoader:
Deploying service providers as modules
A service provider that is developed in a module must be specified in a provides directive in the module declaration. The provides directive specifies both the service and the service provider; this helps to locate the provider when another module, with a uses directive for the service, obtains a service loader for the service. It is strongly recommended that the module does not export the package containing the service provider. There is no support for a module specifying, in a provides directive, a service provider in another module.
[...]
Deploying service providers on the class path
A service provider that is packaged as a JAR file for the class path is identified by placing a provider-configuration file in the resource directory META-INF/services. The name of the provider-configuration file is the fully qualified binary name of the service. The provider-configuration file contains a list of fully qualified binary names of service providers, one per line.
[...]
Basically, if you want to be able to load a service-provider no matter if it's placed on the classpath or modulepath you'll need to specify both a provides directive in the module-info.java file and add the appropriate provider-configuration file under META-INF/services. You also have to make sure you use the correct method for loading the service-providers:
ServiceLoader#load(Class,ClassLoader)
Locates providers in named modules (i.e. modulepath) and unnamed modules (i.e. classpath).
ServiceLoader#load(ModuleLayer,Class)
Only locates providers in named modules (i.e. modulepath).

Related

How to override spring boot starter's default properties in application.properties file?

I created a multi module maven project that contains the library module (spring boot starter application) and application module (spring boot application that have included library as a dependency).
This is the structure of my project:
.
├── application
│   ├── pom.xml
│   └── src
│   ├── main
│   │   ├── kotlin
│   │   │   └── com
│   │   │   └── application
│   │   │   ├── ApplicationService.kt
│   │   │   └── Application.kt
│   │   └── resources
│   │   └── application.properties
│   └── test
│   └── kotlin
│   └── com
│   └── application
│   └── ApplicationServiceTest.kt
├── library
│   ├── pom.xml
│   └── src
│   └── main
│   ├── kotlin
│   │   └── com
│   │   └── application
│   │   ├── LibraryService.kt
│   │   └── Properties.kt
│   └── resources
│   ├── META-INF
│   │   └── spring.factories
│   └── config
│   └── application.properties
└── pom.xml
library/.../Properties.kt:
#ConfigurationProperties("properties")
class Properties {
lateinit var name: String
}
library/.../LibraryService.kt:
#Service
#EnableConfigurationProperties(Properties::class)
class LibraryService(private val properties: Properties) {
fun name() = properties.name
}
library/.../spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.application.LibraryService
library/.../config/application.properties:
properties.name=library
application/.../Application.kt
#SpringBootApplication
class Application
fun main(args: Array<String>) {
runApplication<Application>(*args)
}
application/.../ApplicationService.kt
#Service
class ApplicationService(private val libraryService: LibraryService) {
fun call() = libraryService.name()
}
application/.../application.properties
properties.name=application
So, I have the library module where I put application.properties file with default parameter properties.name=library. The library module has Property class injected in LibraryService. LibraryService has the simple method name() that just returns value from property. I also have application module where I use LibraryService in ApplicationService and invoke name() function, but I have application.properties in application module where properties.name=application.
I expect that application's properties.name=application overrides library's properties.name=library and ApplicationService::call must return value application instead of default value library in properties.name in library module. But this does not happen. ApplicationService::call returns value library.
I created simple junit test to reproduce this behaviour (ApplicationServiceTest.kt):
#SpringBootTest
class ApplicationServiceTest {
#Autowired
lateinit var applicationService: ApplicationService
#Test
fun test() {
println(applicationService.call())
}
}
It prints library. I would like to have the following behaviour: library has some several defined default properties, but I want to be able to override some of these properties in application. How to achieve that?
source code: https://github.com/grolegor/maven-multi-module-spring-starter
Form the documentation
4.2.3. Application Property Files
SpringApplication loads properties from application.properties files in the following locations and adds them to the Spring Environment:
1. A /config subdirectory of the current directory
2. The current directory
3. A classpath /config package
4. The classpath root
The list is ordered by precedence (properties defined in locations higher in the list override those defined in lower locations).
So in your case [library] config/application.properties will be used, because it is higher-ordered then [application] application.properties.
Also, you cannot use application.properties twice.
Looking in your repository I would suggest that you remove the /config/application.properties from the library-module and provide default values in your Properties-class
package com.application
import org.springframework.boot.context.properties.ConfigurationProperties
#ConfigurationProperties("properties")
class Properties {
var name: String = "library"
}

NiFi-1.0.0 - load lua script

I have a NiFi processor, that uses the redislabs/luascript lib in order to load a lua script and execute it on a redis instance.
The thing is that I don't know where exactly to put the lua script in order to load it using the luascript lib. I've put it into the nifi_proc/src/main/resources/lua/name.lua, but I get an IOException.
I have a nifi controller service for connecting to redis and a processor that uses that service.
My project structure:
.
├── nifi-bundle-nar
│   └── target
├── nifi-redis_cservice
│   ├── src
│   └── target
├── nifi-redis_cservice-api
│   ├── src
│   └── target
├── nifi-redis_cservice-api-nar
│   └── target
├── nifi-redis_cservice-nar
│   └── target
├── redis-processors
│   ├── src
│   └── target
└── target
└── maven-shared-archive-resources
Any ideas?
Can you share more information about how the processor is interacting with the library? Are you passing in an InputStream, calling out to a executable, etc.?
Ensure your resource is in the JAR module of your processor's project, not the processor's NAR module or the parent (that includes both). You should be able to use getResourceAsStream("lua/name.lua") from a Class object that is in the processor's JAR file (such as the processor class itself). I'm not sure what you'd need to do with it after that, is it possible to share the source code or more details around it?
EDIT (reply to comments below): fromResource() uses LuaScript's classloader to get the resource, I wonder if it doesn't have access to the nifi-proc or controller service resources. It seems like, unless the user needs to specify the location of the script, that the controller service should be loading in the Lua script. So an alternative could be to use the controller service class to getResourceAsStream, read the whole thing into a String, and use fromSource instead of fromResource.

Can't make a working jar file if all capital letters in Gradle?

I just spent a little while learning the hard way that Gradle can't make a working jar file if the directory (and thus jar file) is labeled something LIKE_THIS.
Does anyone know why this is? I'm just picking up Java and would like to know why. I've heard it's bad practice to label packages with capital letters as well.
EDIT:
If the directory is all caps, I'd get Error: Could not find or load main class src.main.java.HelloWorld but it would work perfectly fine if it's lableded something like_this.
EDIT:
My directory structure after fixing the label was just from a tutorial, but here it is:
alexanders-mbp:VALID_JAVA alexanderkleinhans$ tree
.
├── build
│   ├── classes
│   │   └── main
│   │   └── net
│   │   └── petrikainulainen
│   │   └── gradle
│   │   └── HelloWorld.class
│   ├── dependency-cache
│   ├── libs
│   │   └── VALID_JAVA.jar
│   └── tmp
│   ├── compileJava
│   └── jar
│   └── MANIFEST.MF
├── build.gradle
└── src
└── main
└── java
└── net
└── petrikainulainen
└── gradle
└── HelloWorld.java
Changing the directory to something_else and re-building makes everything work. BTW, no build errors.
EDIT:
I was following this and couldn't get it to work so I finally cloned the git-repo where I discovered my issue.
To run, I would do
gradle assemble
gradle build
java -jar build/library/VALID_JAVA.jar
(VALID_JAVA being the directory name and thus jar file name.)
It looks like you try to launch src.main.java.HelloWorld, but if you look at the sources from the GIT repository the fully qualified class name is net.petrikainulainen.gradle.HelloWorld (which also matches your directory structure).
To launch the application, use
java -classpath build/libs/VALID_JAVA.jar net.petrikainulainen.gradle.HelloWorld
Note also, according to the decription at the GIT repository, the gradle build creates a runnable jar file which you can launch like
java -jar pathToJarFile

Null pointer exception for google api Classpath

I'm a little bit new with Java, so I apologize in advance. In a Google API Quickstart, a .credentials file is loaded from the method getResourceAsStream and the path as "/client_secret.json". From here, I learned that this getResourceAsStream get's the file from the CLASS path, which confuses me because my src directory structure looks like the following.
.
└── main
├── java
│   └── GmailQuickstart.java
└── resources
└── client_secret.json
When I open a new project (using a servlet) and try to load this, I'm finding this InputStream is null. The src directory of the following project looks like so:
.
└── main
├── java
│   ├── GmailQuickstart.java
│   ├── com
│   │   └── something
│   │   └── HelloWorldServlet.java
│   └── somepackage
│   └── Thing.java
├── resources
│   └── client_sercret.json
└── webapp
└── WEB-INF
└── web.xml
I've tried changing it to what I though would be the same CLASSPATH, but I guess that's not correct. From the previous advice, I've also tried "/main/java/resources/client_secret.json". Obviously I'm not doing something right here.
Could someone help point me in the right direction?
Try to add resources folder to your classpath.
If you are using eclipse, right click your project, configure build path, add your resources folder in Source tab.

Overriding a class file from a library JAR in a Java web app

In my Java web app, I have these files:
/WEB-INF/classes/com/example/Foo.class
/WEB-INF/lib/example.jar <-- contains the class "com.example.Foo"
Will the class defined in the classes directory be used instead of the class defined in example.jar?
Thanks.
The answer depends upon your container, it is container dependent. In general, /WEB-INF/classes is preferred to classes in a jar file in WEB-INF/lib.
For Tomcat, the order is the following:
Therefore, from the perspective of a web application, class or resource loading looks in the following repositories, in this order:
Bootstrap classes of your JVM
System class loader classes
/WEB-INF/classes of your web application
/WEB-INF/lib/*.jar of your web application
Common class loader classes
But if you're using a different container, you need to consult the documentation for that server.
So there is only one way to find out. I wrote a test :)
├── example
│   ├── example.iml
│   ├── pom.xml
│   └── src
│   ├── main
│   │   └── java
│   │   └── test
│   │   ├── Bar.java
│   │   └── Foo.java
│   └── test
│   └── java
│   └── testexample
│   └── TestFoo.java
├── pom.xml
├── test.iml
└── web
├── pom.xml
├── src
│   ├── main
│   │   └── java
│   │   └── test
│   │   └── Foo.java
│   └── test
│   └── java
│   └── junittest
│   └── TestFooInWeb.java
└── web.iml
16 directories, 11 files
My finding was that in TestFoo.java prints
Hello from example.jar
Hello from example.jar
And for TestFooInWeb.java prints
Hello from web app
Hello from web app
Both tests have this in the test class:
public class TestFooInWeb/TestFoo {
#Test
public void testHello() {
System.out.println(new Foo().sayHello());
}
#Test
public void testHelloFromBar() {
new Bar().sayHelloForFoo();
}
}
So all at the end, I stand corrected. You apprantly can load a completely different class and all Jar files will use that new class. This makes sense because the ClassLoader will first look at the classes directory. I am not sure if I agree with it because it sounds suspicious and I can overwrite security classes.

Categories