I am using flink in a maven/java project and need to include my configs internally in the created jar.
So, I have added the following in my pom file. This includes all my yml configs (located in src/main/resources folder) in the jar, whose name I will pass as argument while executing.
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.yml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<finalName>${project.artifactId}-${project.version}</finalName>
<shadedArtifactAttached>true</shadedArtifactAttached>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.exmaple.MyApplication</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
The following main class code receives an arg based on which I decide what config to pick from resource, read(using snakeyaml) and use.
public static void main(String[] args) throws Exception {
final ParameterTool parameterTool = ParameterTool.fromArgs(args);
ClassLoader classLoader = MyApplication.class.getClassLoader();
Yaml yaml = new Yaml();
String filename = parameterTool.getRequired("configFilename");
InputStream in = classLoader.getSystemResourceAsStream(filename);
MyConfigClass = yaml.loadAs(in, MyConfigClass.class);
...
}
mvn clean install creates "my-shaded-jar.jar"
which I execute using command
java -jar /path/to/my-shaded-jar.jar --configFilename filename
It works on multiple systems, if I share the jar with others.
However I am facing issue, when I try to run the same jar in a yarn cluster on Hadoop, using the following command:-
HADOOP_CLASSPATH=`hadoop classpath` HADOOP_CONF_DIR=/etc/hadoop/conf ./flink-1.6.2/bin/flink run -m yarn-cluster -yd -yn 5 -ys 30 -yjm 10240 -ytm 10240 -yst -ynm some-job-name -yqu queue-name ./my-shaded-jar.jar --configFilename filename
I am getting following Error:
------------------------------------------------------------
The program finished with the following exception:
org.apache.flink.client.program.ProgramInvocationException: The main method caused an error.
at org.apache.flink.client.program.PackagedProgram.callMainMethod(PackagedProgram.java:546)
at org.apache.flink.client.program.PackagedProgram.invokeInteractiveModeForExecution(PackagedProgram.java:421)
at org.apache.flink.client.program.OptimizerPlanEnvironment.getOptimizedPlan(OptimizerPlanEnvironment.java:83)
at org.apache.flink.client.program.PackagedProgramUtils.createJobGraph(PackagedProgramUtils.java:78)
at org.apache.flink.client.program.PackagedProgramUtils.createJobGraph(PackagedProgramUtils.java:120)
at org.apache.flink.client.cli.CliFrontend.runProgram(CliFrontend.java:238)
at org.apache.flink.client.cli.CliFrontend.run(CliFrontend.java:216)
at org.apache.flink.client.cli.CliFrontend.parseParameters(CliFrontend.java:1053)
at org.apache.flink.client.cli.CliFrontend.lambda$main$11(CliFrontend.java:1129)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAs(Subject.java:422)
at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1754)
at org.apache.flink.runtime.security.HadoopSecurityContext.runSecured(HadoopSecurityContext.java:41)
at org.apache.flink.client.cli.CliFrontend.main(CliFrontend.java:1129)
Caused by: org.yaml.snakeyaml.error.YAMLException: java.io.IOException: Stream closed
at org.yaml.snakeyaml.reader.StreamReader.update(StreamReader.java:200)
at org.yaml.snakeyaml.reader.StreamReader.<init>(StreamReader.java:60)
at org.yaml.snakeyaml.Yaml.loadAs(Yaml.java:444)
at com.example.MyApplication.main(MyApplication.java:53)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.flink.client.program.PackagedProgram.callMainMethod(PackagedProgram.java:529)
... 13 more
Caused by: java.io.IOException: Stream closed
at java.io.PushbackInputStream.ensureOpen(PushbackInputStream.java:74)
at java.io.PushbackInputStream.read(PushbackInputStream.java:166)
at org.yaml.snakeyaml.reader.UnicodeReader.init(UnicodeReader.java:90)
at org.yaml.snakeyaml.reader.UnicodeReader.read(UnicodeReader.java:122)
at java.io.Reader.read(Reader.java:140)
at org.yaml.snakeyaml.reader.StreamReader.update(StreamReader.java:184)
Why does my solution works on any normal linux/mac systems, however the same jar with same args fails when running with flink run command on yarn cluster.
Is there a difference between how we generally execute jars and how yarn does it.
Any help appreciated.
Replace classLoader.getSystemResourceAsStream(filename) with classLoader.getResourceAsStream(filename).
java.lang.ClassLoader#getSystemResourceAsStream locates the resource through the system class loader, which is typically used to start the application.
java.lang.ClassLoader#getResourceAsStream will first search the parent class loader. That failing, it will invoke findResource of the current class loader.
To avoid dependency conflicts, classes in Flink applications are divided into two domains [1], which is also applied to Flink client, e.g. CliFrontend.
The Java Classpath includes the classes of Apache Flink and its core dependencies.
The Dynamic User Code includes the classes (and resources) of user jars.
So in order to find your "config file", which is packaged in your jar file, we should use the user code class loader (you can find the details of userCodeClassLoader in org.apache.flink.client.program.PackagedProgram), instead of the system classloader.
https://ci.apache.org/projects/flink/flink-docs-stable/monitoring/debugging_classloading.html
Related
TL;DR
The grpc-core jar file doesn't work when embedded in a shaded jar but does work when placed on the classpath as a separate jar file. I can't tell if the issue is with grpc-core or grpc-netty (or grpc-netty-shaded) which seems to depend on grpc-core, but when grpc-core is embedded in my shaded jar, I get a java.nio.channels.UnsupportedAddressTypeException exception.
Longer Explanation
Until recently my Java web application (that deploys as a .war file) did not make use of shaded jar files. That is, in the WEB-INF folder contained all the jar files individually laid out. I recently made a change to use a shaded jar and everything works fine except for an integration that makes use of grpc-netty-shaded. I have tried using different versions of the grpc-* libraries and using both the shaded and unshaded version of grpc-netty, but neither of those work when I create a shaded jar.
However, if I produce a shaded jar and place the grpc-core library separately (as a sibling to the shaded jar in the WEB-INF folder) then everything works. I should also say that it doesn't matter if I run the application from within Tomcat or deploy it from the command line, the result is the same. I have also tried switching the JRE I am using but that made no difference.
Here is the exception I get:
Exception in thread "main" io.grpc.StatusRuntimeException: UNKNOWN
at io.grpc.stub.ClientCalls.toStatusRuntimeException(ClientCalls.java:271)
at io.grpc.stub.ClientCalls.getUnchecked(ClientCalls.java:252)
at io.grpc.stub.ClientCalls.blockingUnaryCall(ClientCalls.java:165)
at com.ibm.crypto.grep11.grpc.CryptoGrpc$CryptoBlockingStub.generateKey(CryptoGrpc.java:2964)
at ibm.TestHpcsGrep11.main(TestHpcsGrep11.java:77)
Caused by: java.nio.channels.UnsupportedAddressTypeException
at sun.nio.ch.Net.checkAddress(Unknown Source)
at sun.nio.ch.SocketChannelImpl.connect(Unknown Source)
at io.grpc.netty.shaded.io.netty.util.internal.SocketUtils$3.run(SocketUtils.java:91)
at io.grpc.netty.shaded.io.netty.util.internal.SocketUtils$3.run(SocketUtils.java:88)
at java.security.AccessController.doPrivileged(Native Method)
at io.grpc.netty.shaded.io.netty.util.internal.SocketUtils.connect(SocketUtils.java:88)
at io.grpc.netty.shaded.io.netty.channel.socket.nio.NioSocketChannel.doConnect(NioSocketChannel.java:315)
at io.grpc.netty.shaded.io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.connect(AbstractNioChannel.java:248)
at io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline$HeadContext.connect(DefaultChannelPipeline.java:1342)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeConnect(AbstractChannelHandlerContext.java:548)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.connect(AbstractChannelHandlerContext.java:533)
at io.grpc.netty.shaded.io.netty.channel.ChannelDuplexHandler.connect(ChannelDuplexHandler.java:54)
at io.grpc.netty.shaded.io.grpc.netty.WriteBufferingAndExceptionHandler.connect(WriteBufferingAndExceptionHandler.java:157)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeConnect(AbstractChannelHandlerContext.java:548)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.access$1000(AbstractChannelHandlerContext.java:61)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext$9.run(AbstractChannelHandlerContext.java:538)
at io.grpc.netty.shaded.io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)
at io.grpc.netty.shaded.io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:469)
at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:503)
at io.grpc.netty.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
at io.grpc.netty.shaded.io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.grpc.netty.shaded.io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Unknown Source)
This is the project I am integrating with. While the project makes use of version 1.44.0 of the grpc libraries, I have tried it with 1.44.0, 1.47.0, and 1.48.1 and always get the same result.
Here is the Maven plugin configuration I am using to produce the shaded jar:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<artifactSet>
<excludes>
<exclude>junit:junit</exclude>
<exclude>org.testng:testng</exclude>
<exclude>javax.servlet:servlet-api</exclude>
</excludes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
I did spend hours resolving the same issue, but finally found the root cause.
When running your mvn package command, you most likely get a warning similar to the following:
[WARNING] grpc-core-1.48.1.jar, grpc-netty-shaded-1.48.1.jar define 1 overlapping resource:
[WARNING] - META-INF/services/io.grpc.NameResolverProvider
those 2 files need to be merged (otherwise maven won't take any of them).
this can be done by adding the following to your pom.xml within your maven shade plugin <configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
this will merge your duplicate files and also respect potential relocations from the shade plugin.
also see
https://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html#ServicesResourceTransformer
Maven Shade - change file name and replace text
I have a command line app that downloads some reports, processes them, then uploads data to Google Drive. I'm using Typesafe-Config for all the magic strings I need. Typesafe-Config looks on the classpath for my application.conf file and uses HOCON to map config objects to fields in my class, like this:
From ~/configs/application.conf:
my.homePageUrl = "https://my.home.com"
From MyClass.java:
private static Config config = ConfigFactory.load();
private static final String HOME_URL = config.getString("my.homePageUrl");
I'm using the maven-shade-plugin to build an executable .jar for easy deployment on a remote server. The plugin looks like this:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4</version>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>com.my.reports.ReportRunner</Main-Class>
<Class-Path>~/configs/application.conf</Class-Path>
</manifestEntries>
</transformer>
</transformers>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
The problem is, when I run the executable .jar, application.conf isn't found on my classpath (I guess this also could be a bug in the typesafe code). All this works just fine in Intellij.
dustinevan#iMac:~/bin$ java -jar my-reports-1.0-SNAPSHOT.jar
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: com.typesafe.config.ConfigException$Missing: No configuration setting found for key 'my'
at com.typesafe.config.impl.SimpleConfig.findKey(SimpleConfig.java:124)
at com.typesafe.config.impl.SimpleConfig.find(SimpleConfig.java:147)
at com.typesafe.config.impl.SimpleConfig.find(SimpleConfig.java:159)
at com.typesafe.config.impl.SimpleConfig.find(SimpleConfig.java:164)
at com.typesafe.config.impl.SimpleConfig.getList(SimpleConfig.java:212)
at com.typesafe.config.impl.SimpleConfig.getHomogeneousUnwrappedList(SimpleConfig.java:271)
at com.typesafe.config.impl.SimpleConfig.getStringList(SimpleConfig.java:329)
at com.stellarliving.reports.ecp.ReportRunner.<clinit>(ReportRunner.java:19)
dustinevan#iMac:~/configs$ ls
total 8
-rw-r--r--# 1 dustinevan staff 1520 Jun 13 01:16 application.conf
I've tried MANY permutations, and done lots of reading to solve this, any help would be greatly appreciated.
As I just had the same problem...
The -jar overrides all classpath settings, so only the jar is seen. -Dconfig.trace=loads will show what is seen by java.
We want the application.conf on the classpath, as well as the jar, so:
java -cp .:my-reports-1.0-SNAPSHOT.jar full.path.to.main.Main
did the trick for me. application.conf found and overrides reference.conf in the jar.
I also had that issue. I've noticed that you use a Class-Path declaration in shaded configuration, so I've merged the answer by Lothar with your info and added:
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>com.my.reports.ReportRunner</Main-Class>
<Class-Path>.</Class-Path>
</manifestEntries>
</transformer>
I am building a CLI tool, which integrates with several EJB modules. For this reason, I need to build a fat jar, which is then executed as a standalone application.
However, executing this fat jar with java -jar (Note: conf/openejb.xml is in the same directory as the fat jar) fails with the following stacktrace:
INFORMATION - PersistenceUnit(name=demo, provider=org.hibernate.jpa.HibernatePersistenceProvider) - provider time 2706ms
INFORMATION - Jndi(name="java:global/DemoMain/demo-shade-1.0-SNAPSHOT/MEJB!javax.management.j2ee.ManagementHome")
INFORMATION - Jndi(name="java:global/DemoMain/demo-shade-1.0-SNAPSHOT/MEJB")
INFORMATION - Jndi(name="java:global/DemoMain/demo-shade-1.0-SNAPSHOT/openejb/Deployer!org.apache.openejb.assembler.Deployer")
INFORMATION - Jndi(name="java:global/DemoMain/demo-shade-1.0-SNAPSHOT/openejb/Deployer")
INFORMATION - Jndi(name="java:global/DemoMain/demo-shade-1.0-SNAPSHOT/openejb/ConfigurationInfo!org.apache.openejb.assembler.classic.cmd.ConfigurationInfo")
INFORMATION - Jndi(name="java:global/DemoMain/demo-shade-1.0-SNAPSHOT/openejb/ConfigurationInfo")
INFORMATION - Jndi(name="java:global/DemoMain/demo-shade-1.0-SNAPSHOT/DemoServiceImpl!com.github.rzo1.service.DemoService")
INFORMATION - Jndi(name="java:global/DemoMain/demo-shade-1.0-SNAPSHOT/DemoServiceImpl")
INFORMATION - Existing thread singleton service in SystemInstance(): org.apache.openejb.cdi.ThreadSingletonServiceImpl#557c8e7e
INFORMATION - Closing DataSource: demoDS
INFORMATION - Closing DataSource: demoDSNonJTA
Exception in thread "Thread-0" java.lang.RuntimeException: org.apache.openejb.OpenEjbContainer$AssembleApplicationException: javax.enterprise.inject.spi.DeploymentException: couldn't start owb context
at com.github.rzo1.DemoMain.run(DemoMain.java:116)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.openejb.OpenEjbContainer$AssembleApplicationException: javax.enterprise.inject.spi.DeploymentException: couldn't start owb context
at org.apache.openejb.OpenEjbContainer$Provider.createEJBContainer(OpenEjbContainer.java:346)
at javax.ejb.embeddable.EJBContainer.createEJBContainer(EJBContainer.java:56)
at com.github.rzo1.DemoMain.run(DemoMain.java:90)
... 1 more
Caused by: javax.enterprise.inject.spi.DeploymentException: couldn't start owb context
at org.apache.openejb.cdi.ThreadSingletonServiceImpl.initialize(ThreadSingletonServiceImpl.java:191)
at org.apache.openejb.cdi.CdiBuilder.build(CdiBuilder.java:41)
at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:913)
at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:717)
at org.apache.openejb.OpenEjbContainer$Provider.createEJBContainer(OpenEjbContainer.java:342)
... 3 more
Caused by: org.apache.webbeans.exception.WebBeansException: Wrong startup object.
at org.apache.webbeans.web.lifecycle.WebContainerLifecycle.getServletContext(WebContainerLifecycle.java:227)
at org.apache.webbeans.web.lifecycle.WebContainerLifecycle.startApplication(WebContainerLifecycle.java:86)
at org.apache.openejb.cdi.ThreadSingletonServiceImpl.initialize(ThreadSingletonServiceImpl.java:189)
... 7 more
Executing the code directly from my IDE (IntelliJ) the standalone container comes up and behaves as expected.
Version Summary:
openejb in version 1.7.0 / openejb-server in version 7.0.2
maven-shade-plugin in version 2.4.3
Maven in version 3.3.9
hibernate in version 5.2.7
Basic Setup
I was able to reproduce my problem on a simple working example, which I added as an GitHub project for further investigation.
The basic project layout is as follows:
| # demo-shade
| - demo-services (EJB-Module)
| - demo-main (Shading happens here)
The configuration of the maven-shade-plugin is as follows:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>demo-shade-${project.version}</finalName>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>com.github.rzo1.DemoMain</Main-Class>
</manifestEntries>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/openwebbeans/openwebbeans.properties</resource>
</transformer>
</transformers>
<filters>
<filter> <!-- we don't want JSF to be activated -->
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/faces-config.xml</exclude>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<shadedClassifierName>dist</shadedClassifierName>
</configuration>
</execution>
</executions>
</plugin>
The code to start up the container:
EJBContainer ejbContainer = null;
try {
final Properties properties = new Properties();
properties.setProperty(EJBContainer.APP_NAME, applicationName);
properties.setProperty(EJBContainer.PROVIDER, OpenEjbContainer.class.getName());
properties.setProperty(OpenEjbContainer.OPENEJB_EMBEDDED_REMOTABLE, "false");
properties.setProperty("ejbd.disabled", "true");
properties.setProperty("ejbds.disabled", "true");
properties.setProperty("admin.disabled", "true");
properties.setProperty("openejb.jaxrs.application", "false");
Path launchPath = Paths.get(DemoMain.class.getProtectionDomain().getCodeSource().getLocation().toURI());
properties.setProperty("openejb.configuration", launchPath.toAbsolutePath() + "/conf/openejb.xml");
properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
properties.put("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver");
// This is the line starting the EJB container
ejbContainer = EJBContainer.createEJBContainer(properties);
ejbContainer.getContext().bind("inject", this);
ejbContainerReady = true;
final CountDownLatch latch = new CountDownLatch(1);
// Graceful shutdown
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
try {
logger.info("Shutting down..");
latch.countDown();
logger.info("Shutdown completed successfully.");
} catch (final Exception e) {
logger.error("Graceful shutdown went wrong. SIGKILL (kill -9) if you want.", e);
}
}
});
try {
latch.await();
} catch (final InterruptedException e) {
// ignored
}
} catch (final Exception e) {
ejbContainerReady = false;
throw new RuntimeException(e);
} finally {
if (ejbContainer != null) {
ejbContainer.close();
}
}
}
Questions
Did I miss something in the configuration of the maven-shade-plugin?
How can I build a fat jar using openejb in a standalone manner?
Example Project
The full code example is available as a GitHub Project
UPDATE 1:
I changed the pom according to the answer by P. Merkle. I found an other article here describing the process of shading specifically for TomEE.
pom changed to
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>demo-shade-${project.version}</finalName>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>com.github.rzo1.DemoMain</Main-Class>
</manifestEntries>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.openwebbeans.maven.shade.OpenWebBeansPropertiesTransformer"/>
</transformers>
<filters>
<filter> <!-- we don't want JSF to be activated -->
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/faces-config.xml</exclude>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<shadedClassifierName>dist</shadedClassifierName>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.apache.openwebbeans</groupId>
<artifactId>openwebbeans-maven</artifactId>
<version>1.7.0</version>
</dependency>
</dependencies>
</plugin>
Extecuting this fat jar brings:
INFORMATION - OpenWebBeans Container is starting...
INFORMATION - Adding OpenWebBeansPlugin : [CdiPlugin]
SCHWERWIEGEND - CDI Beans module deployment failed
java.lang.NullPointerException
at org.apache.openejb.cdi.CdiScanner.handleBda(CdiScanner.java:271)
at org.apache.openejb.cdi.CdiScanner.init(CdiScanner.java:148)
at org.apache.openejb.cdi.OpenEJBLifecycle.startApplication(OpenEJBLifecycle.java:179)
at org.apache.openejb.cdi.ThreadSingletonServiceImpl.initialize(ThreadSingletonServiceImpl.java:189)
at org.apache.openejb.cdi.CdiBuilder.build(CdiBuilder.java:41)
at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:913)
at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:717)
at org.apache.openejb.OpenEjbContainer$Provider.createEJBContainer(OpenEjbContainer.java:342)
at javax.ejb.embeddable.EJBContainer.createEJBContainer(EJBContainer.java:56)
at com.github.rzo1.DemoMain.run(DemoMain.java:90)
at java.lang.Thread.run(Unknown Source)
INFORMATION - Closing DataSource: demoDS
INFORMATION - Closing DataSource: demoDSNonJTA
Exception in thread "Thread-0" java.lang.RuntimeException: org.apache.openejb.OpenEjbContainer$AssembleApplicationException: javax.enterprise.inject.spi.DeploymentException: couldn't start owb context
at com.github.rzo1.DemoMain.run(DemoMain.java:116)
at java.lang.Thread.run(Unknown Source)
Caused by: org.apache.openejb.OpenEjbContainer$AssembleApplicationException: javax.enterprise.inject.spi.DeploymentException: couldn't start owb context
at org.apache.openejb.OpenEjbContainer$Provider.createEJBContainer(OpenEjbContainer.java:346)
at javax.ejb.embeddable.EJBContainer.createEJBContainer(EJBContainer.java:56)
at com.github.rzo1.DemoMain.run(DemoMain.java:90)
... 1 more
Caused by: javax.enterprise.inject.spi.DeploymentException: couldn't start owb context
at org.apache.openejb.cdi.ThreadSingletonServiceImpl.initialize(ThreadSingletonServiceImpl.java:191)
at org.apache.openejb.cdi.CdiBuilder.build(CdiBuilder.java:41)
at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:913)
at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:717)
at org.apache.openejb.OpenEjbContainer$Provider.createEJBContainer(OpenEjbContainer.java:342)
... 3 more
Caused by: org.apache.openejb.OpenEJBRuntimeException: java.lang.NullPointerException
at org.apache.openejb.cdi.OpenEJBLifecycle.startApplication(OpenEJBLifecycle.java:200)
at org.apache.openejb.cdi.ThreadSingletonServiceImpl.initialize(ThreadSingletonServiceImpl.java:189)
... 7 more
Caused by: java.lang.NullPointerException
at org.apache.openejb.cdi.CdiScanner.handleBda(CdiScanner.java:271)
at org.apache.openejb.cdi.CdiScanner.init(CdiScanner.java:148)
at org.apache.openejb.cdi.OpenEJBLifecycle.startApplication(OpenEJBLifecycle.java:179)
... 8 more
I added a branch with this changes on the GitHub Project for further investigation.
UPDATE 2
I excluded javax.xml.* from the shade:
<excludes>
<exclude>META-INF/faces-config.xml</exclude>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>javax/xml/**</exclude>
</excludes>
However, the exception remains the same as in Update 1. I pushed a related branch to the GitHub repository.
So my question is:
What else needs to be excluded from the shade?
With the help of the other answers , I was finally able to find a working solution for building a standalone fat jar, which is working for my use-case.
UPDATE 3:
The steps (for now) are:
Usage of OpenWebBeansPropertiesTransformer instead AppendingTransformer as stated by P. Merkle
Exclude java.xml.* in the shade as stated by Romain Manni-Bucau:
<excludes>
<exclude>META-INF/faces-config.xml</exclude>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>javax/xml/**</exclude>
Add a scan.xml in META-INF including only packages / classes, which should be scanned. Current working version can be found here
Question:
Is their an official or better way of doing this?
http://tomee.apache.org/advanced/shading/index.html and maybe http://tomee.apache.org/advanced/applicationcomposer/index.html are also good starting points.
Now it seems you scan unexpected classes like xml one where the classloader is null. Likely exclude javax.xml.* from scanning or even the shade and it will work
The exception is caused by merging multiple openwebbeans.properties files from different modules into a single properties file via AppendingTransformer.
This is because openwebbeans.properties files are structured in a special way:
All those files contain a single property configuration.ordinal which defines their 'importance'. Any setting from a property file with a higher configuration.ordinal will overwrite settings from one with a lower configuration.ordinal.
Now, if you naively merge these files--as AppendingTransformer does-- , you will end up with multiple competing ordinal properties in the same file.
The solution is to replace AppendingTransformer with OpenWebBeansPropertiesTransformer, which preserves priorities while merging.
An example pom.xml is available here: http://openwebbeans.apache.org/meecrowave/meecrowave-maven/index.html
Bad news is, however, that this solution uncovers another exception:
java.lang.NullPointerException at org.apache.openejb.cdi.CdiScanner.handleBda(CdiScanner.java:271)
So far I've not been able to identify the cause of that.
I'm running a .jar file that contains all dependencies that I need packaged in it. One of this dependencies is com.google.common.util.concurrent.RateLimiter and already checked it's class file is in this .jar file.
Unfortunately when I hit the command spark-submit on the master node of my google's dataproc-cluster instance I'm getting this error:
Exception in thread "main" java.lang.NoSuchMethodError: com.google.common.base.Stopwatch.createStarted()Lcom/google/common/base/Stopwatch;
at com.google.common.util.concurrent.RateLimiter$SleepingStopwatch$1.<init>(RateLimiter.java:417)
at com.google.common.util.concurrent.RateLimiter$SleepingStopwatch.createFromSystemTimer(RateLimiter.java:416)
at com.google.common.util.concurrent.RateLimiter.create(RateLimiter.java:130)
at LabeledAddressDatasetBuilder.publishLabeledAddressesFromBlockstem(LabeledAddressDatasetBuilder.java:60)
at LabeledAddressDatasetBuilder.main(LabeledAddressDatasetBuilder.java:144)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.apache.spark.deploy.SparkSubmit$.org$apache$spark$deploy$SparkSubmit$$runMain(SparkSubmit.scala:672)
at org.apache.spark.deploy.SparkSubmit$.doRunMain$1(SparkSubmit.scala:180)
at org.apache.spark.deploy.SparkSubmit$.submit(SparkSubmit.scala:205)
at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:120)
at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
It seems something happened in the sense of overwriting my dependencies. Already decompiled the Stopwatch.class file from this .jar and checked that method is there. That just happened when I ran on that google dataproc instance.
I did grep on the process executing the spark-submit and I got the flag -cp like this:
/usr/lib/jvm/java-8-openjdk-amd64/bin/java -cp /usr/lib/spark/conf/:/usr/lib/spark/lib/spark-assembly-1.5.0-hadoop2.7.1.jar:/usr/lib/spark/lib/datanucleus-api-jdo-3.2.6.jar:/usr/lib/spark/lib/datanucleus-rdbms-3.2.9.jar:/usr/lib/spark/lib/datanucleus-core-3.2.10.jar:/etc/hadoop/conf/:/etc/hadoop/conf/:/usr/lib/hadoop/lib/native/:/usr/lib/hadoop/lib/*:/usr/lib/hadoop/*:/usr/lib/hadoop-hdfs/lib/*:/usr/lib/hadoop-hdfs/*:/usr/lib/hadoop-mapreduce/lib/*:/usr/lib/hadoop-mapreduce/*:/usr/lib/hadoop-yarn/lib/*:/usr/lib/hadoop-yarn/*
Is there anything I can do to solve this problem?
Thank you.
As you've found, Dataproc includes Hadoop dependencies on the classpath when invoking Spark. This is done primarily so that using Hadoop input formats, file systems, etc is fairly straight-forward. The downside is that you will end up with Hadoop's guava version which is 11.02 (See HADOOP-10101).
How to work around this depends on your build system. If using Maven, the maven-shade plugin can be used to relocate your version of guava under a new package name. An example of this can be seen in the GCS Hadoop Connector's packaging, but the crux of it is the following plugin declaration in your pom.xml build section:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<relocation>
<pattern>com.google.common</pattern>
<shadedPattern>your.repackaged.deps.com.google.common</shadedPattern>
</relocation>
</relocations>
</execution>
</execution>
</plugin>
Similar relocations can be accomplished with the sbt-assembly plugin for sbt, jarjar for ant, and either jarjar or shadow for gradle.
I have a app.properties file in my maven project under resources folder as shown here (simplified):
myApp
|----src
| |
| |--main
| |--java
| | |--ApplicationInitializer.java
| |
| |--resources
| |--app.properties
|
|---target
|--myApp.jar
|--app.properties
In ApplicationInitializer class I want to load properties from the app.properties file with following piece of code:
Properties props = new Properties();
String path = "/app.properties";
try {
props.load(ApplicationInitializer.class.getResourceAsStream(path));
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(props.getProperty("property"));
This piece of code loads properties correctly when I run it from inside my IDE but fails with exception
Exception in thread "main" java.lang.NullPointerException
at java.util.Properties$LineReader.readLine(Properties.java:434)
at java.util.Properties.load0(Properties.java:353)
at java.util.Properties.load(Properties.java:341)
at cz.muni.fi.fits.ApplicationInitializer.main(ApplicationInitializer.java:18)
when trying to run in as JAR file.
For creating a jar file I am using combination of maven-shade-plugin, maven-jar-plugin (for excluding properties file outside of the JAR) and maven-resources-plugin (for copying properties file to specific folder) in pom.xml file as shown here:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>cz.muni.fi.fits.ApplicationInitializer</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.5</version>
<configuration>
<excludes>
<exclude>**/*.properties</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>copy-resource</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target</outputDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
I then switched the code in main method to this one:
Properties props = new Properties();
String path = "./app.properties";
try (FileInputStream file = new FileInputStream(path)) {
props.load(file);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(props.getProperty("property"));
and managed to load properties from file when running JAR but this time I was not able to load them when running in IDE, it ended with the same exception as above.
So my question is: How to set the filepath (or pom.xml file?) that I will be able to load properties running from both IDE and JAR file?
Thanks in advance :)
In your Java code read your app.properties file like this:
final String ROOT_PATH = "custom.path"; // Or whatever you most like
final String PROPERTIES_FILE = "app.properties";
// start :: try-catch here to manage I/O resources
File directory = new File(System.getProperty(ROOT_PATH), "conf");
File file = new File(directory, PROPERTIES_FILE);
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file));
// ...
// end :: try-catch here to manage I/O resources
Then you have to ways for setting/declaring custom.path:
Declaring it as an environment variable
Setting it at run-time (environment properties) with the -Dcustom.path=/somewhere/in/your/filesystem (I prefer this one, unless the path is fixed and/or shared across different applications)
Then, eventually you need to copy/put your app.properties file inside /somewhere/in/your/filesystem/conf. And why inside /conf? Because you use it when you declare the directory field. If you don't want it in there just don't set it and delete the , "conf" part.
Additionally, for running it "locally" (in your IDE) use the VM options setting (IntelliJ IDEA):
The way you're working at the moment you depend on the working directory of your java process. Typically that's the your command line (aka shell) points to, when starting the application.
On most IDE's you can configure this directory in your launcher settings. (For eclipse it's on the second tab 'Arguments') So you will need to the target directory (For eclipse there's a button 'Workspace...')
Specifically in intelliJ you can find this setting under Run/Edit Configurations... This will open a window like this:
There you can edit the working directory. You simply add target at the end.
Edit:
Actually you've add src/main/ressources at the end.