Pi4J / pi4j-v2

Pi4J Version 2.0
Apache License 2.0
275 stars 61 forks source link

PWM provider not found #238

Open Ghozti opened 2 years ago

Ghozti commented 2 years ago

Hello, after a few days of not touching my project I came to this error which seemingly happened out of nowhere despite it working numerous times before, com.pi4j.provider.exception.ProviderNotFoundException: Pi4J provider [pigpio-pwm] could not be found. Please include this 'provider' JAR in the classpath. I have not made any changes to the code that "breaks" my program, and it is actually taken right from the pi4j website:

public static com.pi4j.io.pwm.PwmConfig buildPwmConfig(Context pi4j, int address, PwmType type) {//pwm config builder return Pwm.newConfigBuilder(pi4j) .id("BCM" + address) .name("Buzzer") .address(address) .pwmType(type) .provider("pigpio-pwm") .initial(0) .shutdown(0) .build(); }

RLEO94 commented 2 years ago

Hi, did you create a fat jar? I have a similar issue https://github.com/Pi4J/pi4j-v2/issues/237

FDelporte commented 2 years ago

Did you include all dependencies for the PiGpio provider? See https://pi4j.com/documentation/providers/pigpio/

RLEO94 commented 2 years ago

Hi, I have included all the dependecies.

I tried to start a new Maven project like in your description: https://pi4j.com/documentation/building/fat-jar/ With the inputs from your pi4-example-fatjar, I was able to generate a fatjar which works.

With gradle I was not able to generate a working jar file. I had the same Exception like above, with pigpio-output instead of pigpio-pwm(I use GPIO Output and Input in my code).

I looked at these 2 jar Files to find the difference. For the one which is not working:

Could this be the problem? You mentioned (same link in this comment), that the maven-shade-plugin includes the plugins in META-INF/services/com.pi4j.extension.Plugin. and this works But it seems that for a reason gradle is not able to do this correctly

FDelporte commented 2 years ago

Indeed PiGpioPlugin contains pigpio-pwm, so without it this is the expected exception. Is this the place and time to start a discussion about Maven versus Gradle? ;-)

Screenshot 2022-09-28 at 13 47 06

RLEO94 commented 2 years ago

Hi and thanks for your help No thanks it's ok for me :)

Anyway I would like to share my solution. Maybe it's useful for others. I removed the pi4j-plugin-raspberrypi from my build.gradle

When I generated the jar, the line: "com.pi4j.plugin.pigpio.PiGpioPlugin" was inserted in the META-INF/services/com.pi4j.extension.Plugin then it worked

Probably this line was overwritten because of the other plugin

taartspi commented 2 years ago

@FDelporte With changes in the build.gradle the uberJar has all the code you identified above and I figured out how to add the class path to the manifest in case that was the problem But this is useless as it is an uberJar not individual jars.

So I did add a print in the Example code and the classPath is the uberJar.

If you run with verbose you can see the code IS looking in the looking in the uberJar Issue #237 mentions hack of removing the Rpi default code and then the pigpio is added. Can you think of any common attributes Pi4j uses to identify these providers in case there is a duplicate value that is then used as a key, so only one provider is added and not both the Rpi and concrete. And printing the providers does not list these needed pigpio providers.

tasks.register('uberJar', Jar) { archiveClassifier = 'uber' manifest { attributes 'Main-Class': 'com.pi4j.example.MinimalExample' attributes( "Class-Path": configurations.runtimeClasspath.collect { it.getName() }.join(' ')) No value as it lists individual jars } from sourceSets.main.output duplicatesStrategy = DuplicatesStrategy.EXCLUDE dependsOn configurations.runtimeClasspath from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } }

taartspi commented 2 years ago

Found what the bug is, don't understand why The example code prints out the providers.
Changing the Dependencies to the following by moving pigpio above the raspberry entry:

dependencies { implementation 'org.slf4j:slf4j-api:1.7.32' implementation 'org.slf4j:slf4j-simple:1.7.32' implementation 'com.pi4j:pi4j-core:2.1.1' implementation 'com.pi4j:pi4j-plugin-pigpio:2.1.1' implementation 'com.pi4j:pi4j-plugin-raspberrypi:2.1.1' } Now printing the providers hits class not found, comment out the printing providers and the example runs.

@FDelporte I don't know if this is a gradle bug/feature the Pi4j stumbles onto or specific to Pi4j. Your thoughts

taartspi commented 2 years ago

shadow.txt Note: mvn clean package -Ddetail=true Building via maven shows the shadow process kicks in to clean up duplicates. In gradle this requires added configuration changes in the build.gradle. Maybe with those changes the uberJar will work properly. As usual there are many discussion on the correct way to config the shadow, if anyone has a proven means please share.

pi@raspi464alt:~/Example/pi4j-example-minimal $ mvn clean package -Ddetail=true [INFO] Scanning for projects... [WARNING] [WARNING] Some problems were encountered while building the effective model for com.pi4j:pi4j-example-minimal:jar:0.0.1 [WARNING] 'build.plugins.plugin.(groupId:artifactId)' must be unique but found duplicate declaration of plugin org.apache.maven.plugins:maven-jar-plugin @ line 185, column 21 [WARNING] [WARNING] It is highly recommended to fix these problems because they threaten the stability of your build. [WARNING] [WARNING] For this reason, future Maven versions might no longer support building such malformed projects. [WARNING] [INFO] [INFO] -------------------< com.pi4j:pi4j-example-minimal >-------------------- [INFO] Building Pi4J :: MINIMAL EXAMPLE :: Sample minimal project 0.0.1 [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ pi4j-example-minimal --- [INFO] Deleting /home/pi/Example/pi4j-example-minimal/target [INFO] [INFO] --- maven-dependency-plugin:2.8:copy-dependencies (copy-dependencies) @ pi4j-example-minimal --- [INFO] Copying slf4j-api-1.7.32.jar to /home/pi/Example/pi4j-example-minimal/target/distribution/slf4j-api-1.7.32.jar [INFO] Copying jsch-0.1.55.jar to /home/pi/Example/pi4j-example-minimal/target/distribution/jsch-0.1.55.jar [INFO] Copying slf4j-simple-1.7.32.jar to /home/pi/Example/pi4j-example-minimal/target/distribution/slf4j-simple-1.7.32.jar [INFO] Copying pi4j-plugin-pigpio-2.1.1.jar to /home/pi/Example/pi4j-example-minimal/target/distribution/pi4j-plugin-pigpio-2.1.1.jar [INFO] Copying pi4j-core-2.1.1.jar to /home/pi/Example/pi4j-example-minimal/target/distribution/pi4j-core-2.1.1.jar [INFO] Copying pi4j-plugin-raspberrypi-2.1.1.jar to /home/pi/Example/pi4j-example-minimal/target/distribution/pi4j-plugin-raspberrypi-2.1.1.jar [INFO] Copying pi4j-library-linuxfs-2.1.1.jar to /home/pi/Example/pi4j-example-minimal/target/distribution/pi4j-library-linuxfs-2.1.1.jar [INFO] Copying pi4j-plugin-linuxfs-2.1.1.jar to /home/pi/Example/pi4j-example-minimal/target/distribution/pi4j-plugin-linuxfs-2.1.1.jar [INFO] Copying pi4j-library-pigpio-2.1.1.jar to /home/pi/Example/pi4j-example-minimal/target/distribution/pi4j-library-pigpio-2.1.1.jar [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ pi4j-example-minimal --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] skip non existing resourceDirectory /home/pi/Example/pi4j-example-minimal/src/main/resources [INFO] [INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ pi4j-example-minimal --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 4 source files to /home/pi/Example/pi4j-example-minimal/target/classes [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ pi4j-example-minimal --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] skip non existing resourceDirectory /home/pi/Example/pi4j-example-minimal/src/test/resources [INFO] [INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ pi4j-example-minimal --- [INFO] No sources to compile [INFO] [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ pi4j-example-minimal --- [INFO] No tests to run. [INFO] [INFO] --- maven-jar-plugin:3.1.2:jar (default-jar) @ pi4j-example-minimal --- [INFO] Building jar: /home/pi/Example/pi4j-example-minimal/target/distribution/pi4j-example-fatjar.jar [INFO] [INFO] --- maven-shade-plugin:3.2.4:shade (default) @ pi4j-example-minimal --- [INFO] Including org.slf4j:slf4j-api:jar:1.7.32 in the shaded jar. [INFO] Including org.slf4j:slf4j-simple:jar:1.7.32 in the shaded jar. [INFO] Including com.pi4j:pi4j-core:jar:2.1.1 in the shaded jar. [INFO] Including com.pi4j:pi4j-plugin-raspberrypi:jar:2.1.1 in the shaded jar. [INFO] Including com.pi4j:pi4j-plugin-pigpio:jar:2.1.1 in the shaded jar. [INFO] Including com.pi4j:pi4j-library-pigpio:jar:2.1.1 in the shaded jar. [INFO] Including com.pi4j:pi4j-plugin-linuxfs:jar:2.1.1 in the shaded jar. [INFO] Including com.jcraft:jsch:jar:0.1.55 in the shaded jar. [INFO] Including com.pi4j:pi4j-library-linuxfs:jar:2.1.1 in the shaded jar. [WARNING] Discovered module-info.class. Shading will break its strong encapsulation. [WARNING] Discovered module-info.class. Shading will break its strong encapsulation. [WARNING] Discovered module-info.class. Shading will break its strong encapsulation. [WARNING] Discovered module-info.class. Shading will break its strong encapsulation. [WARNING] Discovered module-info.class. Shading will break its strong encapsulation. [WARNING] Discovered module-info.class. Shading will break its strong encapsulation. [WARNING] Discovered module-info.class. Shading will break its strong encapsulation. [WARNING] jsch-0.1.55.jar, pi4j-core-2.1.1.jar, pi4j-example-fatjar.jar, pi4j-library-linuxfs-2.1.1.jar, pi4j-library-pigpio-2.1.1.jar, pi4j-plugin-linuxfs-2.1.1.jar, pi4j-plugin-pigpio-2.1.1.jar, pi4j-plugin-raspberrypi-2.1.1.jar, slf4j-api-1.7.32.jar, slf4j-simple-1.7.32.jar define 1 overlapping resource: [WARNING] - META-INF/MANIFEST.MF [WARNING] pi4j-core-2.1.1.jar, pi4j-library-linuxfs-2.1.1.jar, pi4j-library-pigpio-2.1.1.jar, pi4j-plugin-linuxfs-2.1.1.jar, pi4j-plugin-pigpio-2.1.1.jar, pi4j-plugin-raspberrypi-2.1.1.jar define 3 overlapping resources: [WARNING] - LICENSE.txt [WARNING] - NOTICE.txt [WARNING] - README.md [WARNING] maven-shade-plugin has detected that some class files are [WARNING] present in two or more JARs. When this happens, only one [WARNING] single version of the class is copied to the uber jar. [WARNING] Usually this is not harmful and you can skip these warnings, [WARNING] otherwise try to manually exclude artifacts based on [WARNING] mvn dependency:tree -Ddetail=true and the above output. [WARNING] See http://maven.apache.org/plugins/maven-shade-plugin/ [INFO] Replacing original artifact with shaded artifact. [INFO] Replacing /home/pi/Example/pi4j-example-minimal/target/distribution/pi4j-example-fatjar.jar with /home/pi/Example/pi4j-example-minimal/target/pi4j-example-minimal-0.0.1-shaded.jar [INFO] Dependency-reduced POM written at: /home/pi/Example/pi4j-example-minimal/dependency-reduced-pom.xml [INFO] [INFO] --- maven-antrun-plugin:3.0.0:run (copy) @ pi4j-example-minimal --- [INFO] Executing tasks [INFO] [copy] Copying 1 file to /home/pi/Example/pi4j-example-minimal/target/distribution [INFO] Executed tasks [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 15.241 s [INFO] Finished at: 2022-10-02T11:31:18-05:00 [INFO] ------------------------------------------------------------------------ pi@raspi464alt:~/Example/pi4j-example-minimal $ mvn clean package -Ddetail=true

@FDelporte

FDelporte commented 2 years ago

I'm a Maven user, so, unfortunately, my Gradle knowledge is very limited... The only Gradle we have in Pi4J was also contributed by others to prove it also works with Pi4J V2, see https://github.com/Pi4J/pi4j-example-minimal/blob/main/build.gradle.

BTW I'm very interested in the Maven output you mentioned about the clean up, can you add it to your comment?

toboche commented 10 months ago

The issue comes from the fact that different Pi4J maven modules come with different values defined in META-INF/services/com.pi4j.extension.Plugin files. Depending on how you have your fat jar built, this might result in, by default, overriding the other values provided in this file by other modules.

I am using "com.github.johnrengelman.shadow" plugin to build the fat jar. I had to add

tasks.withType<ShadowJar> {
    mergeServiceFiles()
}

in my build.gradle file. With this, while the fat jar is built, whenever new content is defined for INF/services/com.pi4j.extension.Plugin thet values are merged, not overriden.

FDelporte commented 10 months ago

Thanks @toboche for the info! Can you please validate if this Gradle example is correct? It doesn't use that Shadow plugin, but when we created this minimal example, it was working as expected...

https://github.com/Pi4J/pi4j-example-minimal/blob/main/build.gradle

toboche commented 10 months ago

hey @FDelporte , when running ./gradlew build locally on my Mac and then uploading the .jar to my Raspberry and running it via sudo java -jar /home/toboche/pi4j-example-minimal-0.0.1.jar I'm getting:

no main manifest attribute, in /home/toboche/pi4j-example-minimal-0.0.1.jar

When running ./gradlew build on my Raspberry via sudo java -jar /home/toboche/pi4j-example-minimal-0.0.1.jar I'm getting:

Could not compile settings file '/home/toboche/pi4j-example-minimal/settings.gradle'.
> startup failed:
  General error during semantic analysis: Unsupported class file major version 61
toboche commented 10 months ago

Having fixed the issue of missing manifest, I'm getting:

Exception in thread "main" java.lang.NoClassDefFoundError: com/pi4j/util/Console
    at com.pi4j.example.MinimalExample.main(MinimalExample.java:60)
Caused by: java.lang.ClassNotFoundException: com.pi4j.util.Console
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
    ... 1 more

Let me know if you'd like me to open a PR with some fixes for that in the coming weeks :)

Ir3nicuz commented 3 months ago

I found this thread at googling for a similar issue.

At compiling/bundeling a jar package with Gradle there seems to be a wierd issue with plugins. Gradle seems to bundle all plugins listed at the dependencies to the jar package, but at least override or not append some "links" to it. That is a problem at using more then one provider, therefore more then one plugin is needed.

My programm uses provider pigpio and linuxfs. My Gradle dependencies look like:

dependencies { api 'org.slf4j:slf4j-api:2.0.12' api 'com.pi4j:pi4j-core:2.6.0' api 'com.pi4j:pi4j-plugin-raspberrypi:2.6.0' api 'com.pi4j:pi4j-plugin-pigpio:2.6.0' api 'com.pi4j:pi4j-plugin-linuxfs:2.6.0' api 'com.pi4j:pi4j-plugin-gpiod:2.6.0' }

When I run the programm, I got "ProviderNotFoundException for pigpio".

If I change the Gradle dependencies to:

dependencies { api 'org.slf4j:slf4j-api:2.0.12' api 'com.pi4j:pi4j-core:2.6.0' api 'com.pi4j:pi4j-plugin-pigpio:2.6.0' api 'com.pi4j:pi4j-plugin-linuxfs:2.6.0' api 'com.pi4j:pi4j-plugin-gpiod:2.6.0' api 'com.pi4j:pi4j-plugin-raspberrypi:2.6.0' }

And I run the programm, I got "ProviderNotFoundException for linuxfs".

But the plugin folder in the .jar package looks at both dependency cases like this (all plugins present) : package

Are there new ideas to this problem? It seems a little bit wierd to have to learn/configure/use a third party plugin like shadow for Gradle to be able to bundle JavaPlugins?!

Ir3nicuz commented 3 months ago

Digging through the web to the main problem of not merged services files by Gradle, tired of using plugins over plugins for solving every little problem, found this neat solution at http://cmoz.me/blog/2014/11/service-files-uber-jars-and-gradle/

This can be added to every Gradle standard "Jar" Task. A Fat-Jar build with Gradle with this task addition runs all Pi4J provider services in parallel:

` tasks.register('dist', Jar) {

    group = BasePlugin.BUILD_GROUP
    dependsOn classes
    dependsOn configurations.runtimeClasspath

    manifest {
        attributes 'Main-Class': project.mainClassName
    }

    doFirst {
        // prepare the buildDir as temporary storage directrory
        def serviceDir = file("$buildDir/META-INF/services")
        serviceDir.deleteDir()
        serviceDir.mkdirs()

        // copy all service files from dependencies to buildDir, merge content if name matches
        for(file in configurations.runtimeClasspath) {
            zipTree(file).matching{ include 'META-INF/services/*' }.each { f ->
                new File(serviceDir, f.name) << f.getText("UTF-8")
            }
        }
    }

    // Merge all Dependencies to one fat-Jar, but exclude the service files
    duplicatesStrategy(DuplicatesStrategy.EXCLUDE)
    from(configurations.runtimeClasspath.collect{ it.isDirectory() ? it : zipTree(it) }) {
        exclude 'META-INF/services/*' 
    }

    // Include the merged service files from the buildDir
    from fileTree(buildDir).matching{ include 'META-INF/services/*' }

    with jar
}`