When trying to use PF4J i created the necessary parts as outlined in
https://github.com/pf4j/pf4j
an Interface that extends the ExtensionPoint
a Plugin
Jar with Manifest
Plugin load and activation
Why is the List of clickHandlers empty?
I have tested this with a JUnit test where I can debug the other parts which seem to work fine. See debug log below.
I have also looked at https://github.com/pf4j/pf4j/issues/21 and activated the Eclipse annotation processing with no positive effect.
1. Interface that extends the Extension Point
public interface ClickHandler extends ExtensionPoint {
...
}
2. a Plugin
public class MBClickHandlerPlugin extends Plugin {
/**
* construct me
* #param wrapper
*/
public MBClickHandlerPlugin(PluginWrapper wrapper) {
super(wrapper);
}
#Extension
public static class MBClickHandler implements ClickHandler {
}
}
3. Jar with Manifest
unzip -q -c target/com.bitplan.mb-0.0.1.jar META-INF/MANIFEST.MF
Manifest-Version: 1.0
Plugin-Dependencies:
Plugin-Id: com.bitplan.mb
Built-By: wf
Plugin-Provider: BITPlan GmbH
Plugin-Version: 0.0.1
Plugin-Class: com.bitplan.mb.MBClickHandlerPlugin
Created-By: Apache Maven 3.5.2
Build-Jdk: 1.8.0_152
4. Plugin load and activation
/**
* activate the plugins requested on the command line
*/
public void activatePlugins() {
pluginManager = new DefaultPluginManager();
for (String plugin : plugins) {
Path pluginPath = Paths.get(plugin);
pluginManager.loadPlugin(pluginPath);
}
pluginManager.startPlugins();
List<ClickHandler> clickHandlers = pluginManager
.getExtensions(ClickHandler.class);
for (ClickHandler clickHandler : clickHandlers) {
installClickHandler(clickHandler);
}
}
Debug log
22 [main] DEBUG org.pf4j.CompoundPluginDescriptorFinder - Try to continue with the next finder
22 [main] DEBUG org.pf4j.CompoundPluginDescriptorFinder - 'org.pf4j.ManifestPluginDescriptorFinder#73d4cc9e' is applicable for plugin '/Users/wf/Documents/workspace/com.bitplan.mb/target/com.bitplan.mb-0.0.1.jar'
24 [main] DEBUG org.pf4j.AbstractPluginManager - Found descriptor PluginDescriptor [pluginId=com.bitplan.mb, pluginClass=com.bitplan.mb.MBClickHandlerPlugin, version=0.0.1, provider=BITPlan GmbH, dependencies=[], description=, requires=*, license=null]
24 [main] DEBUG org.pf4j.AbstractPluginManager - Class 'com.bitplan.mb.MBClickHandlerPlugin' for plugin '/Users/wf/Documents/workspace/com.bitplan.mb/target/com.bitplan.mb-0.0.1.jar'
24 [main] DEBUG org.pf4j.AbstractPluginManager - Loading plugin '/Users/wf/Documents/workspace/com.bitplan.mb/target/com.bitplan.mb-0.0.1.jar'
24 [main] DEBUG org.pf4j.CompoundPluginLoader - 'org.pf4j.DefaultPluginLoader#6366ebe0' is not applicable for plugin '/Users/wf/Documents/workspace/com.bitplan.mb/target/com.bitplan.mb-0.0.1.jar'
24 [main] DEBUG org.pf4j.CompoundPluginLoader - 'org.pf4j.JarPluginLoader#44f75083' is applicable for plugin '/Users/wf/Documents/workspace/com.bitplan.mb/target/com.bitplan.mb-0.0.1.jar'
25 [main] DEBUG org.pf4j.PluginClassLoader - Add 'file:/Users/wf/Documents/workspace/com.bitplan.mb/target/com.bitplan.mb-0.0.1.jar'
25 [main] DEBUG org.pf4j.AbstractPluginManager - Loaded plugin '/Users/wf/Documents/workspace/com.bitplan.mb/target/com.bitplan.mb-0.0.1.jar' with class loader 'org.pf4j.PluginClassLoader#43d7741f'
25 [main] DEBUG org.pf4j.AbstractPluginManager - Creating wrapper for plugin '/Users/wf/Documents/workspace/com.bitplan.mb/target/com.bitplan.mb-0.0.1.jar'
25 [main] DEBUG org.pf4j.AbstractPluginManager - Created wrapper 'PluginWrapper [descriptor=PluginDescriptor [pluginId=com.bitplan.mb, pluginClass=com.bitplan.mb.MBClickHandlerPlugin, version=0.0.1, provider=BITPlan GmbH, dependencies=[], description=, requires=*, license=null], pluginPath=/Users/wf/Documents/workspace/com.bitplan.mb/target/com.bitplan.mb-0.0.1.jar]' for plugin '/Users/wf/Documents/workspace/com.bitplan.mb/target/com.bitplan.mb-0.0.1.jar'
26 [main] DEBUG org.pf4j.DependencyResolver - Graph:
com.bitplan.mb -> []
26 [main] DEBUG org.pf4j.DependencyResolver - Plugins order: [com.bitplan.mb]
27 [main] INFO org.pf4j.AbstractPluginManager - Plugin 'com.bitplan.mb#0.0.1' resolved
27 [main] INFO org.pf4j.AbstractPluginManager - Start plugin 'com.bitplan.mb#0.0.1'
27 [main] DEBUG org.pf4j.DefaultPluginFactory - Create instance for plugin 'com.bitplan.mb.MBClickHandlerPlugin'
28 [main] DEBUG org.pf4j.AbstractExtensionFinder - Finding extensions of extension point 'com.bitplan.uml2mxgraph.ClickHandler'
28 [main] DEBUG org.pf4j.LegacyExtensionFinder - Reading extensions storages from classpath
28 [main] DEBUG org.pf4j.AbstractExtensionFinder - No extensions found
28 [main] DEBUG org.pf4j.LegacyExtensionFinder - Reading extensions storages from plugins
28 [main] DEBUG org.pf4j.LegacyExtensionFinder - Reading extensions storage from plugin 'com.bitplan.mb'
28 [main] DEBUG org.pf4j.LegacyExtensionFinder - Cannot find 'META-INF/extensions.idx'
28 [main] DEBUG org.pf4j.AbstractExtensionFinder - No extensions found
28 [main] DEBUG org.pf4j.AbstractExtensionFinder - Finding extensions of extension point 'com.bitplan.uml2mxgraph.ClickHandler' for plugin 'null'
28 [main] DEBUG org.pf4j.AbstractExtensionFinder - Finding extensions of extension point 'com.bitplan.uml2mxgraph.ClickHandler' for plugin 'com.bitplan.mb'
29 [main] DEBUG org.pf4j.AbstractExtensionFinder - Found 0 extensions for extension point 'com.bitplan.uml2mxgraph.ClickHandler
'
Workaround #1
use a customized PluginManager
pluginManager = new JarPluginManager(this.getClass().getClassLoader());
from the class that will use the plugin to make sure the same classloader is used
JarPluginManager source code:
import java.nio.file.Path;
import org.pf4j.DefaultPluginManager;
import org.pf4j.JarPluginLoader;
import org.pf4j.ManifestPluginDescriptorFinder;
import org.pf4j.PluginClassLoader;
import org.pf4j.PluginDescriptor;
import org.pf4j.PluginDescriptorFinder;
import org.pf4j.PluginLoader;
import org.pf4j.PluginManager;
/**
* see https://github.com/pf4j/pf4j/issues/249 see
* https://pf4j.org/doc/class-loading.html
*
* #author wf
*
*/
public class JarPluginManager extends DefaultPluginManager {
public static class ParentClassLoaderJarPluginLoader extends JarPluginLoader {
static ClassLoader parentClassLoader;
/**
*
* #param pluginManager
*/
public ParentClassLoaderJarPluginLoader(PluginManager pluginManager) {
super(pluginManager);
}
static PluginClassLoader pluginClassLoader;
#Override
public ClassLoader loadPlugin(Path pluginPath,
PluginDescriptor pluginDescriptor) {
if (pluginClassLoader == null) {
boolean parentFirst=true;
pluginClassLoader = new PluginClassLoader(pluginManager,
pluginDescriptor, parentClassLoader,parentFirst);
}
pluginClassLoader.addFile(pluginPath.toFile());
return pluginClassLoader;
}
}
/**
* construct me with the given classloader
* #param classLoader
*/
public JarPluginManager(ClassLoader classLoader) {
ParentClassLoaderJarPluginLoader.parentClassLoader=classLoader;
//System.setProperty("pf4j.mode", RuntimeMode.DEPLOYMENT.toString());
//System.setProperty("pf4j.mode", RuntimeMode.DEVELOPMENT.toString());
}
#Override
protected PluginLoader createPluginLoader() {
// load only jar plugins
return new ParentClassLoaderJarPluginLoader(this);
}
#Override
protected PluginDescriptorFinder createPluginDescriptorFinder() {
// read plugin descriptor from jar's manifest
return new ManifestPluginDescriptorFinder();
}
}
Workaround #2
If the extensions.idx file is not created there is something wrong with your annotation processing. You might want to fix the source of the problem but it is also possible to try to work around it:
https://groups.google.com/forum/#!topic/pf4j/nn20axJHpfI
pointed me to creating the META-INF/extensions.idx file manually and making sure there is a no args constructor for the static inner class. With this change things work.
Watch out for setting the classname correctly in the extensions.idx
file - otherwise you'll end up with a null entry in the handler list
Watch out for having a null argument constructor otherwise you'll endup with an exception
#Extension
public static class MBClickHandler implements ClickHandler {
/**
* constructor with no argument
*/
public MBClickHandler() {
}
src/main/resources/META-INF/extensions.idx
com.bitplan.mb.MBClickHandlerPlugin$MBClickHandler
Code to check
Correct name for extension.idx entry
MBClickHandler ch=new MBClickHandler();
File extFile=new File("src/main/resources/META-INF/extensions.idx");
String extidx=FileUtils.readFileToString(extFile,"UTF-8");
assertEquals(extidx,ch.getClass().getName());
checking the extensions
List<PluginWrapper> startedPlugins = pluginManager.getStartedPlugins();
for (PluginWrapper plugin : startedPlugins) {
String pluginId = plugin.getDescriptor().getPluginId();
System.out.println(String.format("Extensions added by plugin '%s':", pluginId));
Set<String> extensionClassNames = pluginManager.getExtensionClassNames(pluginId);
for (String extension : extensionClassNames) {
System.out.println(" " + extension);
}
}
Related
I run gradle script with dependecy to jupiter,wicket spring boot start and springboot starter
buildscript {
repositories {
mavenCentral()
// The following is only necessary if you want to use SNAPSHOT releases.
// maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
}
dependencies {
classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.2'
}
}
apply plugin: 'org.junit.platform.gradle.plugin'
// Apply the java-library plugin to add support for Java Library
apply plugin: 'java'
junitPlatform {
reportsDir file('build/test-results/junit-platform') // this is the default
enableStandardTestTask true
// selectors (optional)
// filters (optional)
}
// In this section you declare where to find the dependencies of your project
repositories {
// Use jcenter for resolving your dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
}
dependencies {// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter
compile group: 'org.springframework.boot', name: 'spring-boot-starter', version: '1.5.9.RELEASE'
// https://mvnrepository.com/artifact/com.giffing.wicket.spring.boot.starter/wicket-spring-boot-starter
compile group: 'com.giffing.wicket.spring.boot.starter', name: 'wicket-spring-boot-starter', version: '2.0.4'
}
dependencies {
testCompile("org.junit.jupiter:junit-jupiter-api:5.0.2")
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.2")
}
dependencies {
testCompile("junit:junit:4.12")
testRuntime("org.junit.vintage:junit-vintage-engine:4.12.2")
}
On the test
package steinKo.ATM.Web.test.unit;
import org.apache.wicket.util.tester.WicketTester;
import org.junit.jupiter.api.Test;
class HomePageTest {
private WicketTester tester;
#Test
public void shouldRender() {
tester = new WicketTester();
tester.startPage(HomePage.class);
tester.assertRenderedPage(HomePage.class);
}
}
On the class
package steinKo.ATM.Web;
import org.apache.wicket.markup.html.WebPage;
public class HomePage extends WebPage{
/**
*
*/
private static final long serialVersionUID = 1L;
}
With the html
Insert
title here
I recive folloing error message
2:57:15.185 [main] DEBUG
org.apache.wicket.core.util.resource.locator.ResourceStreamLocator -
Attempting to locate resource 'org/apache/wicket/Page.html' using
finder'[webapppath: /]' 22:57:15.185 [main] DEBUG
org.apache.wicket.core.util.resource.locator.ResourceStreamLocator -
Attempting to locate resource 'org/apache/wicket/Page.html' using
finder'[classpath: META-INF/resources/]' 22:57:15.186 [main] DEBUG
org.apache.wicket.markup.MarkupCache - Markup not found:
steinKo.ATM.Web.HomePage_nb_NO.html 22:57:15.195 [main] DEBUG
org.apache.wicket.page.PageAccessSynchronizer - 'main' attempting to
acquire lock to page with id '0' 22:57:15.195 [main] DEBUG
org.apache.wicket.page.PageAccessSynchronizer - main acquired lock to
page 0 22:57:15.229 [main] WARN RequestCycleExtra -
******************************** 22:57:15.232 [main] WARN RequestCycleExtra - Handling the following exception
org.apache.wicket.markup.MarkupNotFoundException: Can not determine
Markup. Component is not yet connected to a parent. [Page class =
steinKo.ATM.Web.HomePage, id = 0, render count = 1]
I think the erreor is cause by lack of link between Homepage.java and HomePage.hlml in Gradle
How could I fixed this error?
The solution
sourceSets {
main {
java {
srcDirs = ['src/main/java']
}
resources {
srcDirs = ['src/main/java','src/main/resources']
}
}
}
Hi StackOverflow community,
I am new to writing custom rules for java in SonarQube.
I am writing a custom rule to detect certain interfaces being in used via maven. However, during testing, "!unknownSymbol!" came out instead of the Interface classes being implemented by the test case.
In my test case, I have written a class which implements 2 interfaces: -
- java.util.List
- org.osgi.framework.BundleActivator
The test can detect both interfaces, but when I convert it into a symbolTree then use the method fullyQualifiedName(), it returns "!unknownSymbol!". However, when I am doing a simple toString() it returns the interface name.
I would like to know how to correctly convert it into a symbolTree object and get the correct interface without encountering "!unknownSymbol!"
Thank you very much.
The code:
package org.sonar.samples.java.checks;
import com.google.common.collect.ImmutableList;
import org.sonar.check.Priority;
import org.sonar.check.Rule;
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
import org.sonar.plugins.java.api.tree.ClassTree;
import org.sonar.plugins.java.api.tree.ListTree;
import org.sonar.plugins.java.api.tree.Tree;
import org.sonar.plugins.java.api.tree.TypeTree;
import java.util.List;
#Rule(
key = "AvoidInterfaceClass",
name = "No Interfaces",
description = "Not allowed to implement any interface(s)",
priority = Priority.MAJOR
)
public class AvoidSuperInterfacesRule extends IssuableSubscriptionVisitor {
public static final List<String> SUPER_INTERFACE_AVOID = ImmutableList.of("org.osgi.framework.BundleActivator","java.util.List");
#Override
public List<Tree.Kind> nodesToVisit() {
return ImmutableList.of(Tree.Kind.CLASS);
}
public void visitNode(Tree tree){
//System.out.println("Here");
ClassTree treeClass = (ClassTree) tree;
if (treeClass.superInterfaces().isEmpty()) {
return;
}
ListTree<TypeTree> superInterfaceNames = treeClass.superInterfaces();
System.out.println(superInterfaceNames.size()); //Number of interfaces
System.out.println();
for (TypeTree t:superInterfaceNames){
String name = t.symbolType().fullyQualifiedName();
System.out.println(name);
System.out.println(t.toString());
System.out.println();
if (SUPER_INTERFACE_AVOID.contains(name)){
reportIssue(t, "This interface is not allowed to be implemented.");
}
}
}
}
The output:
09:18:36.332 [main] INFO org.sonar.squidbridge.ProgressReport - 1 source files to be analyzed
09:18:36.439 [main] DEBUG org.sonar.java.bytecode.ClassLoaderBuilder - ----- Classpath analyzed by Squid:
09:18:36.439 [main] DEBUG org.sonar.java.bytecode.ClassLoaderBuilder - C:\Users\attanyg1\Desktop\Sonarqube\java-custom-rules\target\test-jars\commons-collections4-4.0.jar
09:18:36.439 [main] DEBUG org.sonar.java.bytecode.ClassLoaderBuilder - C:\Users\attanyg1\Desktop\Sonarqube\java-custom-rules\target\test-jars\javaee-api-6.0.jar
09:18:36.439 [main] DEBUG org.sonar.java.bytecode.ClassLoaderBuilder - C:\Users\attanyg1\Desktop\Sonarqube\java-custom-rules\target\test-jars\spring-context-4.3.3.RELEASE.jar
09:18:36.440 [main] DEBUG org.sonar.java.bytecode.ClassLoaderBuilder - C:\Users\attanyg1\Desktop\Sonarqube\java-custom-rules\target\test-jars\spring-web-4.3.3.RELEASE.jar
09:18:36.440 [main] DEBUG org.sonar.java.bytecode.ClassLoaderBuilder - C:\Users\attanyg1\Desktop\Sonarqube\java-custom-rules\target\test-jars\spring-webmvc-4.3.3.RELEASE.jar
09:18:36.440 [main] DEBUG org.sonar.java.bytecode.ClassLoaderBuilder - C:\Users\attanyg1\Desktop\Sonarqube\java-custom-rules\target\test-classes
09:18:36.440 [main] DEBUG org.sonar.java.bytecode.ClassLoaderBuilder - -----
2 -> Number of Interfaces
!unknownSymbol! -> symbolType.getFullyQualifiedName()
BundleActivator -> toString()
!unknownSymbol! -> symbolType.getFullyQualifiedName()
List -> toString()
09:18:36.798 [Report about progress of Java AST analyzer] INFO org.sonar.squidbridge.ProgressReport - 1/1 source files have been analyzed
I am using the Matrikon OPC Server for Simulation and Testing, instead of TOPServer, along with the tutorial HowToStartWithUtgard. I am not able to connect to the server. This is the error that I get:
15:02:18.452 [main] DEBUG o.j.dcom.transport.JIComTransport - Socket closed... Socket[unconnected] host XXX.XXX.XX.X, port 135
15:02:18.453 [main] WARN org.jinterop.dcom.core.JIComServer - Got the class not registered exception , will attempt setting entries based on status flags...
15:02:18.468 [main] INFO org.openscada.opc.lib.da.Server - Failed to connect to server
org.jinterop.dcom.common.JIException: Class not registered. If you are using a DLL/OCX , please make sure it has "DllSurrogate" flag set. Faq A(6) in readme.html. [0x80040154]
at org.jinterop.dcom.core.JIComServer.init(Unknown Source) ~[org.openscada.jinterop.core_2.0.8.201303051454.jar:na]
at org.jinterop.dcom.core.JIComServer.initialise(Unknown Source) ~[org.openscada.jinterop.core_2.0.8.201303051454.jar:na]
at org.jinterop.dcom.core.JIComServer.<init>(Unknown Source) ~[org.openscada.jinterop.core_2.0.8.201303051454.jar:na]
at org.openscada.opc.lib.da.Server.connect(Server.java:117) ~[org.openscada.opc.lib_1.0.0.201303051455.jar:na]
at com.matrikonopc.utgard.tutorial.UtgardReadTutorial.main(UtgardReadTutorial.java:31) [bin/:na]
Caused by: org.jinterop.dcom.common.JIRuntimeException: Class not registered. If you are using a DLL/OCX , please make sure it has "DllSurrogate" flag set. Faq A(6) in readme.html. [0x80040154]
at org.jinterop.dcom.core.JIRemActivation.read(Unknown Source) ~[org.openscada.jinterop.core_2.0.8.201303051454.jar:na]
at ndr.NdrObject.decode(Unknown Source) ~[org.openscada.jinterop.deps_1.0.0.201303051454.jar:na]
at rpc.ConnectionOrientedEndpoint.call(Unknown Source) ~[org.openscada.jinterop.deps_1.0.0.201303051454.jar:na]
at rpc.Stub.call(Unknown Source) ~[org.openscada.jinterop.deps_1.0.0.201303051454.jar:na]
... 5 common frames omitted
15:02:18.469 [main] INFO org.openscada.opc.lib.da.Server - Destroying DCOM session...
15:02:18.470 [main] INFO org.openscada.opc.lib.da.Server - Destroying DCOM session... forked
80040154: Unknown error (80040154)
15:02:18.499 [OPCSessionDestructor] DEBUG org.openscada.opc.lib.da.Server - Starting destruction of DCOM session
15:02:18.500 [OPCSessionDestructor] INFO org.jinterop.dcom.core.JISession - About to destroy 0 sessesion which are linked to this session: 1325311425
15:02:18.500 [OPCSessionDestructor] INFO o.j.dcom.core.JIComOxidRuntime - destroySessionOIDs for session: 1325311425
15:02:18.500 [OPCSessionDestructor] INFO org.openscada.opc.lib.da.Server - Destructed DCOM session
15:02:18.501 [OPCSessionDestructor] INFO org.openscada.opc.lib.da.Server - Session destruction took 27 ms
I do not know where I should register the Class and what Class it refers to.
It is referring to the clsid you're attempting to use -- it is not in the registry. Can you double check that you're using the correct one for Matrikon OPC Simulation Server?
Working demo, tested on Windows 10 and Java 8.
User must have administrator rights on Windows.
Errors that might occur:
00000005: Login error (does the user has administrator rights !?)
8001FFFF: Firewall, RPC dynamic ports are not open (see below)
80040154: Double check CLSID in registry, below HKEY_CLASSES_ROOT
Firewall rules
netsh advfirewall firewall add rule^
name="DCOM-dynamic"^
dir=in^
action=allow^
protocol=TCP^
localport=RPC^
remoteport=49152-65535
rem the next one does not seems needed
netsh advfirewall firewall add rule name="DCOM" dir=in action=allow protocol=TCP localport=135
Java code
package demo.opc;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openscada.opc.lib.common.ConnectionInformation;
import org.openscada.opc.lib.da.AccessBase;
import org.openscada.opc.lib.da.DataCallback;
import org.openscada.opc.lib.da.Item;
import org.openscada.opc.lib.da.ItemState;
import org.openscada.opc.lib.da.Server;
import org.openscada.opc.lib.da.SyncAccess;
public class UtgardReaderDemo {
/**
* Main application, arguments are provided as system properties, e.g.<br>
* java -Dhost="localhost" -Duser="admin" -Dpassword="secret" -jar demo.opc.jar<br>
* Tested with a windows user having administrator rights<br>
* #param args unused
* #throws Exception in case of unexpected error
*/
public static void main(String[] args) throws Exception {
Logger.getLogger("org.jinterop").setLevel(Level.ALL); // Quiet => Level.OFF
final String host = System.getProperty("host", "localhost");
final String user = System.getProperty("user", System.getProperty("user.name"));
final String password = System.getProperty("password");
// Powershell: Get-ItemPropertyValue 'Registry::HKCR\Matrikon.OPC.Simulation.1\CLSID' '(default)'
final String clsId = System.getProperty("clsId", "F8582CF2-88FB-11D0-B850-00C0F0104305");
final String itemId = System.getProperty("itemId", "Saw-toothed Waves.Int2");
final ConnectionInformation ci = new ConnectionInformation(user, password);
ci.setHost(host);
ci.setClsid(clsId);
final Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor());
server.connect();
final AccessBase access = new SyncAccess(server, 1000);
access.addItem(itemId, new DataCallback() {
public void changed(final Item item, final ItemState state) {
System.out.println(state);
}
});
access.bind();
Thread.sleep(10_000L);
access.unbind();
}
}
build.gradle
plugins {
id 'java-library'
id 'eclipse'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.bouncycastle:bcprov-jdk15on:1.60'
implementation 'org.openscada.utgard:org.openscada.opc.lib:1.5.0'
}
jar {
manifest {
attributes(
'Class-Path': configurations.runtimeClasspath.collect { 'lib/' + it.getName() }.join(' '),
'Main-Class': 'demo.opc.UtgardReaderDemo'
)
}
}
assemble {
dependsOn 'dependenciesCopy'
}
task dependenciesCopy(type: Copy) {
group 'dependencies'
from sourceSets.main.compileClasspath
into "$libsDir/lib"
}
What is the problem when I execute the maven command in the loop ? The goal is to update the version of pom.xml of the list of bundles. The first iteration, maven execute correctly (update pom.xml), but it makes error for all item after.
for (String bundlePath: bundlesToUpdate)
{
MavenCli cli = new MavenCli();
String[] arguments = {
"-Dtycho.mode=maven",
"org.eclipse.tycho:tycho-versions-plugin:set-version",
"-DgenerateBackupPoms=false",
"-DnewVersion=" + version};
int result = cli.doMain(arguments, bundlePath, System.out, System.err);
}
Same error with the code:
`MavenCli cli = new MavenCli();
for (String bundlePath: bundlesToUpdate)
{
String[] arguments = {
"-Dtycho.mode=maven",
"org.eclipse.tycho:tycho-versions-plugin:set-version",
"-DgenerateBackupPoms=false",
"-DnewVersion=" + version};
int result = cli.doMain(arguments, bundlePath, System.out, System.err);
}`
First time, it's ok:
[main] INFO org.eclipse.tycho.versions.manipulation.PomManipulator - pom.xml//project/version: 2.2.6-SNAPSHOT => 2.2.7-SNAPSHOT
[main] INFO org.apache.maven.cli.event.ExecutionEventLogger - ------------------------------------------------------------------------
[main] INFO org.apache.maven.cli.event.ExecutionEventLogger - Reactor Summary:
[main] INFO org.apache.maven.cli.event.ExecutionEventLogger -
[main] INFO org.apache.maven.cli.event.ExecutionEventLogger - XXXX project ....................... SUCCESS [ 0.216 s]
[main] INFO org.apache.maven.cli.event.ExecutionEventLogger - com.sungard.valdi.bus.fixbroker.client.bnp ........ SKIPPED
[main] INFO org.apache.maven.cli.event.ExecutionEventLogger - XXX project Feature ...................... SKIPPED
[main] INFO org.apache.maven.cli.event.ExecutionEventLogger - ------------------------------------------------------------------------
[main] INFO org.apache.maven.cli.event.ExecutionEventLogger - BUILD SUCCESS
After the errors are:
[main] ERROR org.apache.maven.cli.MavenCli - Error executing Maven.
[main] ERROR org.apache.maven.cli.MavenCli - java.util.NoSuchElementException
role: org.apache.maven.eventspy.internal.EventSpyDispatcher
roleHint:
[main] ERROR org.apache.maven.cli.MavenCli - Caused by: null
[main] ERROR org.apache.maven.cli.MavenCli - Error executing Maven.
[main] ERROR org.apache.maven.cli.MavenCli - java.util.NoSuchElementException
role: org.apache.maven.eventspy.internal.EventSpyDispatcher
roleHint:
The solution I found is to use Maven Invoker and it works fine for the same functionality:
public class MavenInvoker {
public static void main(String[] args) throws IOException, NoHeadException, GitAPIException
{
MavenInvoker toto = new MavenInvoker();
toto.updateVersionMavenInvoker("2.2.8-SNAPSHOT", "TECHNICAL\\WEB" );
}
private InvocationRequest request = new DefaultInvocationRequest();
private DefaultInvoker invoker = new DefaultInvoker();
public InvocationResult updateVersionMavenInvoker(String newVersion, String folderPath)
{
InvocationResult result = null;
request.setPomFile( new File(folderPath+"\\pom.xml" ) );
String version = "-DnewVersion="+newVersion;
request.setGoals( Arrays.asList("-Dtycho.mode=maven",
"org.eclipse.tycho:tycho-versions-plugin:set-version",
"-DgenerateBackupPoms=false",
version) );
try {
result = invoker.execute( request );
} catch (MavenInvocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
}
This works for me inside a custom maven plugin (using Maven 3.5.0):
ClassRealm classRealm = (ClassRealm) Thread.currentThread().getContextClassLoader();
MavenCli cli = new MavenCli(classRealm.getWorld());
cli.doMain( ... );
The plexus Launcher sets the context class loader to its ClassRealm, which has access to the "global" ClassWorld.
Not sure how stable that solution is, but so far looking good.
Used imports:
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.apache.maven.cli.MavenCli;
See the email thread for a more detail explanation: https://dev.eclipse.org/mhonarc/lists/sisu-users/msg00063.html
It seems the correct way is to give MainCli a ClassWorld instance on construction so it can maintain a proper state through multiple calls.
Example:
final ClassWorld classWorld = new ClassWorld("plexus.core", getClass().getClassLoader());
MavenCli cli = new MavenCli(classWorld);
String[] arguments = {
"-Dtycho.mode=maven",
"org.eclipse.tycho:tycho-versions-plugin:set-version",
"-DgenerateBackupPoms=false",
"-DnewVersion=" + version};
int result = cli.doMain(arguments, bundlePath, System.out, System.err);
I have a JAR file where all my code is archived for running. I have to access a properties file which need to be changed/edited before each run. I want to keep the properties file in the same directory where the JAR file is. Is there anyway to tell Java to pick up the properties file from that directory ?
Note: I do not want to keep the properties file in home directory or pass the path of the properties file in command line argument.
So, you want to treat your .properties file on the same folder as the main/runnable jar as a file rather than as a resource of the main/runnable jar. In that case, my own solution is as follows:
First thing first: your program file architecture shall be like this (assuming your main program is main.jar and its main properties file is main.properties):
./ - the root of your program
|__ main.jar
|__ main.properties
With this architecture, you can modify any property in the main.properties file using any text editor before or while your main.jar is running (depending on the current state of the program) since it is just a text-based file. For example, your main.properties file may contain:
app.version=1.0.0.0
app.name=Hello
So, when you run your main program from its root/base folder, normally you will run it like this:
java -jar ./main.jar
or, straight away:
java -jar main.jar
In your main.jar, you need to create a few utility methods for every property found in your main.properties file; let say the app.version property will have getAppVersion() method as follows:
/**
* Gets the app.version property value from
* the ./main.properties file of the base folder
*
* #return app.version string
* #throws IOException
*/
import java.util.Properties;
public static String getAppVersion() throws IOException{
String versionString = null;
//to load application's properties, we use this class
Properties mainProperties = new Properties();
FileInputStream file;
//the base folder is ./, the root of the main.properties file
String path = "./main.properties";
//load the file handle for main.properties
file = new FileInputStream(path);
//load all the properties from this file
mainProperties.load(file);
//we have loaded the properties, so close the file handle
file.close();
//retrieve the property we are intrested, the app.version
versionString = mainProperties.getProperty("app.version");
return versionString;
}
In any part of the main program that needs the app.version value, we call its method as follows:
String version = null;
try{
version = getAppVersion();
}
catch (IOException ioe){
ioe.printStackTrace();
}
I did it by other way.
Properties prop = new Properties();
try {
File jarPath=new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().getPath());
String propertiesPath=jarPath.getParentFile().getAbsolutePath();
System.out.println(" propertiesPath-"+propertiesPath);
prop.load(new FileInputStream(propertiesPath+"/importer.properties"));
} catch (IOException e1) {
e1.printStackTrace();
}
Get Jar file path.
Get Parent folder of that file.
Use that path in InputStreamPath with your properties file name.
There's always a problem accessing files on your file directory from a jar file. Providing the classpath in a jar file is very limited. Instead try using a bat file or a sh file to start your program. In that way you can specify your classpath anyway you like, referencing any folder anywhere on the system.
Also check my answer on this question:
making .exe file for java project containing sqlite
I have a similar case: wanting my *.jar file to access a file in a directory next to said *.jar file. Refer to THIS ANSWER as well.
My file structure is:
./ - the root of your program
|__ *.jar
|__ dir-next-to-jar/some.txt
I'm able to load a file (say, some.txt) to an InputStream inside the *.jar file with the following:
InputStream stream = null;
try{
stream = ThisClassName.class.getClass().getResourceAsStream("/dir-next-to-jar/some.txt");
}
catch(Exception e) {
System.out.print("error file to stream: ");
System.out.println(e.getMessage());
}
Then do whatever you will with the stream
This works for me. Load your properties file from current directory.
Attention: The method Properties#load uses ISO-8859-1 encoding.
Properties properties = new Properties();
properties.load(new FileReader(new File(".").getCanonicalPath() + File.separator + "java.properties"));
properties.forEach((k, v) -> {
System.out.println(k + " : " + v);
});
Make sure, that java.properties is at the current directory . You can just write a little startup script that switches into to the right directory in before, like
#! /bin/bash
scriptdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd $scriptdir
java -jar MyExecutable.jar
cd -
In your project just put the java.properties file in your project root, in order to make this code work from your IDE as well.
I have an example of doing both by classpath or from external config with log4j2.properties
package org.mmartin.app1;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.LogManager;
public class App1 {
private static Logger logger=null;
private static final String LOG_PROPERTIES_FILE = "config/log4j2.properties";
private static final String CONFIG_PROPERTIES_FILE = "config/config.properties";
private Properties properties= new Properties();
public App1() {
System.out.println("--Logger intialized with classpath properties file--");
intializeLogger1();
testLogging();
System.out.println("--Logger intialized with external file--");
intializeLogger2();
testLogging();
}
public void readProperties() {
InputStream input = null;
try {
input = new FileInputStream(CONFIG_PROPERTIES_FILE);
this.properties.load(input);
} catch (IOException e) {
logger.error("Unable to read the config.properties file.",e);
System.exit(1);
}
}
public void printProperties() {
this.properties.list(System.out);
}
public void testLogging() {
logger.debug("This is a debug message");
logger.info("This is an info message");
logger.warn("This is a warn message");
logger.error("This is an error message");
logger.fatal("This is a fatal message");
logger.info("Logger's name: "+logger.getName());
}
private void intializeLogger1() {
logger = LogManager.getLogger(App1.class);
}
private void intializeLogger2() {
LoggerContext context = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false);
File file = new File(LOG_PROPERTIES_FILE);
// this will force a reconfiguration
context.setConfigLocation(file.toURI());
logger = context.getLogger(App1.class.getName());
}
public static void main(String[] args) {
App1 app1 = new App1();
app1.readProperties();
app1.printProperties();
}
}
--Logger intialized with classpath properties file--
[DEBUG] 2018-08-27 10:35:14.510 [main] App1 - This is a debug message
[INFO ] 2018-08-27 10:35:14.513 [main] App1 - This is an info message
[WARN ] 2018-08-27 10:35:14.513 [main] App1 - This is a warn message
[ERROR] 2018-08-27 10:35:14.513 [main] App1 - This is an error message
[FATAL] 2018-08-27 10:35:14.513 [main] App1 - This is a fatal message
[INFO ] 2018-08-27 10:35:14.514 [main] App1 - Logger's name: org.mmartin.app1.App1
--Logger intialized with external file--
[DEBUG] 2018-08-27 10:35:14.524 [main] App1 - This is a debug message
[INFO ] 2018-08-27 10:35:14.525 [main] App1 - This is an info message
[WARN ] 2018-08-27 10:35:14.525 [main] App1 - This is a warn message
[ERROR] 2018-08-27 10:35:14.525 [main] App1 - This is an error message
[FATAL] 2018-08-27 10:35:14.525 [main] App1 - This is a fatal message
[INFO ] 2018-08-27 10:35:14.525 [main] App1 - Logger's name: org.mmartin.app1.App1
-- listing properties --
dbpassword=password
database=localhost
dbuser=user
Here if you mention .getPath() then that will return the path of Jar and I guess
you will need its parent to refer to all other config files placed with the jar.
This code works on Windows. Add the code within the main class.
File jarDir = new File(MyAppName.class.getProtectionDomain().getCodeSource().getLocation().getPath());
String jarDirpath = jarDir.getParent();
System.out.println(jarDirpath);
File parentFile = new File(".");
String parentPath = file.getCanonicalPath();
File resourceFile = new File(parentPath+File.seperator+"<your config file>");