LeftoverDataException,依赖包,apache license 2.0

1. poi3.9 LeftoverDataException

org.apache.poi.hssf.record.RecordInputStream$LeftoverDataException: Initialisation of record 0x1D left 1 bytes remaining still to be read.
at org.apache.poi.hssf.record.RecordInputStream.hasNextRecord(RecordInputStream.java:156)
at org.apache.poi.hssf.record.RecordFactoryInputStream.nextRecord(RecordFactoryInputStream.java:231)
at org.apache.poi.hssf.record.RecordFactory.createRecords(RecordFactory.java:443)
...

以前的随笔也提到过解决办法http://www.cnblogs.com/starRebel/p/5067026.html。

但是最近在优化读写excel部分的代码,发现上述方法有个缺点就是如果excel有多个sheet,那么转成csv之后只能保留其中的一个sheet,这对于通用性是个很大的缺陷。

但是excel和wps都能正确打开,所以继续搜索可能的办法。先试了试最新的3.15和3.16beta版本,仍然有这个问题。

接着在https://bz.apache.org/bugzilla/show_bug.cgi?id=47251看到了歪果仁讨论这个Exception。

What I have done for the moment is modify the hasNextRecord() method of the RecordInputStream class, so that it simply skips extra data bytes instead of throwing an exception.
But perhaps an experienced POI developer will have a better idea.

看来修改源代码是个可行的方案。定位到抛异常的地方,

public boolean hasNextRecord() throws LeftoverDataException {
if (_currentDataLength != -1 && _currentDataLength != _currentDataOffset) {
throw new LeftoverDataException(_currentSid, remaining());
}
if (_currentDataLength != DATA_LEN_NEEDS_TO_BE_READ) {
_nextSid = readNextSid();
}
return _nextSid != INVALID_SID_VALUE;
}

打算先修改源码试试,注释掉抛出异常的地方

    public boolean hasNextRecord() throws LeftoverDataException {
if (_currentDataLength != -1 && _currentDataLength != _currentDataOffset) {
// throw new LeftoverDataException(_currentSid, remaining());
}
if (_currentDataLength != DATA_LEN_NEEDS_TO_BE_READ) {
_nextSid = readNextSid();
}
return _nextSid != INVALID_SID_VALUE;
}

2. 重新打包

使用的IDE是intelliJ idea community 2016.1版本。以前打包都是通过maven依葫芦画瓢的完成,但是这次下载的poi-3.9-sources.jar文件里没有pom,按照https://search.maven.org上的pom文件没折腾成功,所以打算使用IDE原始的方式。

先获取到poi-3.9依赖的包,我的做法是通过maven获取,先创建一个新的maven工程,然后替换pom文件内容为https://search.maven.org/remotecontent?filepath=org/apache/poi/poi/3.9/poi-3.9.pom的内容,这时maven会自动下载好依赖,以及依赖的依赖。以下就是poi-3.9所有的依赖。

LeftoverDataException,依赖包,apache license 2.0

有了这些依赖包之后,回到修改源代码的工程,先在工程目录下建立一个libs文件夹,再从maven本地仓库中把上图的依赖包复制到libs文件夹中(所有依赖包都复制到libs目录下是为了方便管理和查看)。

LeftoverDataException,依赖包,apache license 2.0

在“Project Structure”设置中,导入依赖包,图中modifySource是工程名,libs是新建的放置依赖包的目录

LeftoverDataException,依赖包,apache license 2.0

再把poi的原代码拷贝到工程中,

LeftoverDataException,依赖包,apache license 2.0

这时在IDE中就能看到工程目前的结构

LeftoverDataException,依赖包,apache license 2.0

接下来就要开始编译了,进入“Project Structure”设置,选择“Artifacts”选项,新建

LeftoverDataException,依赖包,apache license 2.0

点击“OK”之后,出现下图

LeftoverDataException,依赖包,apache license 2.0

当时也没考虑太多,只想着不想要依赖包的内容,所以把依赖包的代码都去掉了

LeftoverDataException,依赖包,apache license 2.0

点击确定之后,再“Build”->“Build Artifacts”,会生成一个新的包,改名字为“poi-modified.jar”。

因为这是本地修改的,所以使用“poi-modified.jar”时就不能通过maven了,需要使用IDE导入包。过程和上面提到过的一样(是否需要导入此修改包的依赖包,后面会提到),这时再运行处理excel程序,就没有报错。

在查看源代码,会发现

public boolean hasNextRecord() throws RecordInputStream.LeftoverDataException {
if(this._currentDataLength != -1 && this._currentDataLength != this._currentDataOffset) {
;
} if(this._currentDataLength != -1) {
this._nextSid = this.readNextSid();
} return this._nextSid != -1;
}

添加注释的哪一行已经没有了,估计是被编译器优化了。

但是,以上对源程序作出的修改很有可能导致别的问题,但是针对目前的问题,是可以这么做的。

--->插曲:重新编译之后,测试时还是报了一个错:“resource 'functionMetadata.txt' not found”。搜索一番之后,发现是在ss\formula\function路径下缺少了functionMetadata.txt文件(https://bz.apache.org/bugzilla/show_bug.cgi?id=51534)。

这才发现,网上的poi-3.9源代码编译出来,比起poi-3.9.jar包还是会缺少一些文件。那就采用缺什么补什么的办法,把functionMetadata.txt文件加到路径下,再编译测试,通过。

3. 依赖,编译

重新思考了一遍流程之后,发现一个问题:因为我在构建“poi-modified.jar”的时候并没有把依赖包放进去,并且在“poi-modified.jar”中也没有看到依赖包的代码,那么如果我在使用“poi-modified.jar”的函数时候间接调用了依赖包中的函数,会不会失败?

有了这个疑惑之后,打算做一个小的测试检测一下。

a.新建一个工程,如下图

LeftoverDataException,依赖包,apache license 2.0

有两个静态函数,一个使用了poi功能,一个没有。

1. 测试不包含依赖包情况:即构建的时候不包含依赖包的源码,构建a中的项目,取名“testDependency.jar”。

然后再创建另一个工程使用“testDependency.jar”包:

LeftoverDataException,依赖包,apache license 2.0

Main.main(null)函数创建了Workbook类,而“testDependency.jar”并没有包含poi包,所以报错如下:

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/poi/hssf/usermodel/HSSFWorkbook
at Main.main(Main.java:10)
at SMain.main(SMain.java:6)
Caused by: java.lang.ClassNotFoundException: org.apache.poi.hssf.usermodel.HSSFWorkbook
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 2 more

说明并没有找到poi的信息。

而如果调用succ()函数,则不报错正常执行。

public class SMain {
public static void main(String[] args) {
Main.succ();
}
}

b. 如果“testDependency.jar”构建时包含poi包代码,则以上两个函数都能正确执行。

综上,感觉就有点奇怪了,似乎java的编译过程和linux下c文件的编译链接过程不一样,有点动态链接的味道?

谷歌搜索一下,结合http://www.360doc.com/content/14/0218/23/9440338_353675002.shtml还有https://www.zhihu.com/question/29125656两篇内容,大致分析如下:

a). 编译时需要依赖包的文件,如果依赖包是已经编译好的.class文件,则直接引用。

b). java使用到类的时候才会加载类,不使用,则不会加载,默认从引用的包中加载。

所以,构建“testDependency.jar”时必须要有poi源文件。而使用“testDependency.jar”时,因为已经是编译好的,所以会直接引用,而不会再次编译。而调用Main.succ()函数,因为没有涉及到poi类,所以不会报错。同样的,如果excel处理程序没有用到依赖的类中的函数或类,也不会有问题,但是建议最好把依赖包都加上。

4. 如果仍然想要使用maven帮助下载依赖包,会有一个问题:

LeftoverDataException,依赖包,apache license 2.0

LeftoverDataException,依赖包,apache license 2.0

poi-ooxml-3.9依赖包中含有poi-3.9,所以maven会下载poi-3.9。“poi-3.9”和“poi-3.9-modified”含有同包同名的类,那么编译的时候会使用哪个jar包呢?如果使用“poi-3.9-modified”,那么符合我们的期望。但是如果使用“poi-3.9”,则我们做的修改就没有用了。

在IntelliJ中,可以指定加载顺序:

LeftoverDataException,依赖包,apache license 2.0

如上图,加载顺序从上到下,哪个jar包在前面就会优先选择这个jar包。

5. apache license

最后,在poi-3.9-sources.jar包META-INF目录下看到了License文件

LeftoverDataException,依赖包,apache license 2.0

正好结合上一篇随笔,实践一下开源协议。

poi使用的是apache2.0协议,约束之一是

 (b) You must cause any modified files to carry prominent notices
stating that You changed the files; and

也就是说,如果修改了文件,需要在被修改的文件中说明。这个一时半会也不知道怎么说明比较好,继续谷歌之,找到一个http://softwareengineering.stackexchange.com/questions/220068/file-with-apache-2-0-and-my-modifications。

有个回答说一了个方法:

You must make it clear the the file has been changed. The easiest way is to simply add your copyright after the original ones:

   Modifications copyright (C) 2013 <your company/name>

If you did the modifications on behalf of your company, then that is in most cases also the name that you must put on the copyright notice

在没有更好说明的情况下打算采取这个形式,修改了RecordInputStream.java文件,在文件头加上:

/* ====================================================================
Modifications copyright 2016 Simba
Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language governing
permissions and limitations under the License.
==================================================================== */

在“poi-3.9-sources.jar”的LICENSE文件(也就是apache 2.0协议)中也有如下一段话

APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives. Copyright [yyyy] [name of copyright owner]

正好目录下有一个NOTICE文件,依葫芦画瓢,添加一段。

此处相当于这个包是使用apache license 2.0版本了:),如果使用别的协议还要另外再研究研究。

Simba POI
Copyright 2016 Simba This product were originally based on Apache POI
Copyright 2009 The Apache Software Foundation This product includes software developed by
The Apache Software Foundation (http://www.apache.org/). This product contains the DOM4J library (http://www.dom4j.org).
Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved. This product contains parts that were originally based on software from BEA.
Copyright (c) 2000-2003, BEA Systems, <http://www.bea.com/>. This product contains W3C XML Schema documents. Copyright 2001-2003 (c)
World Wide Web Consortium (Massachusetts Institute of Technology, European
Research Consortium for Informatics and Mathematics, Keio University) This product contains the Piccolo XML Parser for Java
(http://piccolo.sourceforge.net/). Copyright 2002 Yuval Oren. This product contains the chunks_parse_cmds.tbl file from the vsdump program.
Copyright (C) 2006-2007 Valek Filippov (frob@df.ru)

其实也不知道写的对不对,先这么弄着,等哪天了解的更上一层楼的时候再回来看看也不迟。

然后把这些文件打包到新构建的“testDependency.jar”中。

另外,“poi-3.9-sources.jar”使用许多小组件,如果小组件的开源协议不同,在LICENSE文件里面也包含了不同的协议说明:

APACHE POI SUBCOMPONENTS:

Apache POI includes subcomponents with separate copyright notices and
license terms. Your use of these subcomponents is subject to the terms
and conditions of the following licenses: Office Open XML schemas (ooxml-schemas-1.1.jar) The Office Open XML schema definitions used by Apache POI are
a part of the Office Open XML ECMA Specification (ECMA-376, [1]).
As defined in section 9.4 of the ECMA bylaws [2], this specification
is available to all interested parties without restriction:
.......
.......

总之,先这样了,只能理解到这一步。以后再慢慢加深理解。

上一篇:quartz定时任务-job


下一篇:【leetcode❤python】226. Invert Binary Tree