由提交storm项目jar包引发对jar的原理的探索

序:在开发storm项目时,提交项目jar包当把依赖的第三方jar包都打进去提交storm集群启动时报了发现多个同名的文件错误由此开始了一段对jar包的深刻理解之路。

java.lang.RuntimeException: Found multiple defaults.yaml resources.

You're probably bundling the Storm jars with your topology jar.

[jar:file:/home/hadoop/app/storm/lib/storm-core-0.9.6.jar!/defaults.yaml,

jar:file:/home/hadoop/stormApi-wordcount-1.0-SNAPSHOT-jar-with-dependencies.jar!/defaults.yaml]

这里说明stom集群环境中有storm的jar包,我们提交的jar包里面也包含storm的jar包,在读取配置文件时,发现有一样的文件冲突了导致启动错误。

新浪微博:intsmaze刘洋洋哥

eclipse打jar包

代码如下:

package cn.intsmaze;
public class A {
public static void main(String[] args) {
System.out.println(args[0]);
System.out.println("java工程打jar包");
}
}
 
package cn.intsmaze;
import java.util.HashMap;
import java.util.Map;
import redis.clients.jedis.Jedis;
public class demo {
public static void main(String[] args)
{
System.out.println("java工程调用第三方jar包");
Jedis jedis = new Jedis("localhost", 6379);
Map map=new HashMap();
map.put("11", "1");
d.jedis.hmset("33", map);
d.jedis.close();
}
}

上面的代码的java工程如下:

使用eclipse把该工程打包成jar包:

选择这个jar包的入口类

把上面代码打包为A.jar后,eclipse会自动为我们生成下面这个文件位于META-INF:

MANIFEST.MF文件

Manifest-Version: 1.0

Main-Class: cn.intsmaze.A   这里指定入口类

把上面代码打包为B.jar

MANIFEST.MF文件

Manifest-Version: 1.0

Main-Class: cn.intsmaze.demo  这里指定入口类

因为这里引用了工程lib下面第三方的jar包,但是该jar包并不在classpath路径下面,所有就没有找到该类。

在MANIFEST.MF文件增加calsspath值即可。

Manifest-Version: 1.0

Main-Class: cn.intsmaze.demo

Class-Path: lib/jedis-2.8.0.jar 

然后我们把jedis-2.8.0.jar放到我们B.jar的同一级目录下即可。

这里成功运行了。

注意:

manifest.mf文件定义如下所示:

Manifest-Version: 1.0

Main-Class: com.Task

Class-Path: lib/dom4j-1.6.1.jar lib/jaxen-1.1-beta-7.jar

注意:

<1> manifest.mf文件最后一行必须是一个空行。
<2> lib/dom4j-1.6.1.jar和lib/jaxen-1.1-beta-7.jar之间用一个空格隔开。
<3>每个冒号后有一个空格。

Maven打包:

代码如下:

package cn.intsmaze;
public class A {
public static void main(String[] args) {
System.out.println(args[0]);
System.out.println("java工程打jar包");
}
}
 
package cn.intsmaze;
import java.util.HashMap;
import java.util.Map;
import redis.clients.jedis.Jedis;
public class demo {
public static void main(String[] args)
{
System.out.println("java工程调用第三方jar包");
Jedis jedis = new Jedis("localhost", 6379);
Map map=new HashMap();
map.put("11", "1");
d.jedis.hmset("33", map);
d.jedis.close();
}
}

如上代码当他们的pom文件中只配置了以下属性时:

 <dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.7.3</version>
</dependency>
</dependencies>

这个时候使用maven打包。观察他的目录结构。

这个jar包里面没有包含依赖的jedis的jar包,且manifest.mf文件中也没有指定入口类和Class-Path(该程序到哪里去加载它依赖的jedis.jar包)。

如果要成功运行这个jar包,我们要在manifest.mf设置Main-Class和Class-Path。

方式二:我们可以在pom文件中设置把工程打jar包时把它依赖的jar包也打进来,同时指定Main-Class。

 <build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>cn.intsmaze.demo.RedisDemo</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>

观察他的目录结构。

同时也会打一个没有带依赖的jar包(效果就和没添加插件设置一样)

在打包storm工程时的问题:

 <dependencies>
<dependency>
<groupId>org.apache.storm</groupId>
<artifactId>storm-core</artifactId>
<version>0.9.5</version>
</dependency>
</dependencies>

这个项目只会依赖jdk的jar包和storm的jar包,不依赖其他第三方jar包,我们把这个工程打出jar包,根据上面很明显我们知道jar包中不包含依赖的storm的jar包,且manifest.mf文件中也没有指定Main-Class和Class-Path。

但是把它提交到storm集群中,它是会运行的,这是因为stom集群的Class-Path的路径有jdk和storm的jar包了(我们使用java -jar命令就是jdk什么的。)。关于Class-Path我可以理解的,但是没有指定入口类,它是怎么启动的,我还要慢慢研究。

当上面的storm工程需要依赖第三方的mysql包时,我们必须在pom文件中要求把依赖的jar包打进来,不然我们要在manifest.mf指明它要到哪里去加载依赖的mysql包,同时还要把mysql包上传到我们打的jar包将要运行在那台机器上的指定目录让他可以根据Class-Path加载进去。

<dependencies>
<dependency>
<groupId>org.apache.storm</groupId>
<artifactId>storm-core</artifactId>
<version>0.9.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.27</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>cn.intsmaze.helloworld.WordCountTopologyMain</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>

这里把依赖的第三方jar包都打进去了。

但是我们提交该jar包到storm集群中,报了如下错误:

java.lang.RuntimeException: Found multiple defaults.yaml resources.
You're probably bundling the Storm jars with your topology jar.
[jar:file:/home/hadoop/app/storm/lib/storm-core-0.9.6.jar!/defaults.yaml,
jar:file:/home/hadoop/stormApi-wordcount-1.0-SNAPSHOT-jar-with-dependencies.jar!/defaults.yaml]

这里说明stom集群环境中有storm的jar包,我们提交的jar包里面也包含storm的jar包,在读取配置文件时,发现有一样的文件冲突了导致启动错误。

这个时候我们设置如下:

<dependency>
<groupId>org.apache.storm</groupId>
<artifactId>storm-core</artifactId>
<scope>provided</scope>期望JDK、容器或使用者会提供这个依赖
<version>0.9.5</version>
</dependency>

这个时候不会把依赖的storm的包打进工程中,只会把依赖的mysql包打进来。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏zingpLiu

浅析Python多线程

学习Python多线程的资料很多,吐槽Python多线程的博客也不少。本文主要介绍Python多线程实际应用,且假设读者已经了解多线程的基本概念。如果读者对进程...

1918
来自专栏orientlu

FreeRTOS 消息队列

上面这几中方式中, 除了消息通知, 其他几种实现都是基于消息队列。消息队列作为主要的通信方式, 支持在任务间, 任务和中断间传递消息内容。 这一章介绍 Fre...

3502
来自专栏技术博客

菜菜从零学习WCF七(消息协定)

    另一种常见方案是定义消息头和正文的安全属性,也就是说,确定是否对这些元素进行数字签名和加密。消息样式的操作可提供这种控制。

813
来自专栏张善友的专栏

自动类型安全的.NET标准REST库refit

在SCOTT HANSELMAN 博客上看到一个好东西《Exploring refit, an automatic type-safe REST library...

1947
来自专栏Android相关

Android---SharedPreferences解析

SharedPreferences真正实现的类是:SharedPreferencesImpl

1563
来自专栏Android 研究

Android系统启动——3init.rc解析

init.rc文件是以“块”(section)为单位服务的,,一个“块”(section)可以包含多行。“块”(section)分成两大类:一类称为"动作(ac...

3222
来自专栏java一日一条

ava多线程:volatile变量、happens-before关系及内存一致性

请参考来自 Jean-philippe Bempel 的评论。他提到了一个真实因 JVM 优化导致死锁的例子。我尽可能多地写博客的原因之一是一旦自己理解错了,可...

732
来自专栏程序员的碎碎念

ThinkPHP基础知识(三)

TP调试模式: 入口文件index.php中: define('APP_DEBUG',true); //默认为false,表示关闭...

3839
来自专栏haifeiWu与他朋友们的专栏

Redis协议规范(译文)

Redis客户端使用名为RESP(Redis序列化协议)的协议与Redis服务器进行通信。 虽然该协议是专为Redis设计的,但它可以用于其他CS软件项目的通讯...

1493
来自专栏求索之路

java并发编程实战笔记(部分实战未看,老旧章节跳过)

终于把这本经典的Java并发书看完了,虽然之前看的Thinking in Java和Effective Java里面都有并发的章节,但是这本书讲的更加深入,并...

41010

扫码关注云+社区