forked from liferay/liferay-portal
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
LPS-63269 Remove Elasticsearch's duplicate class check
elastic/elasticsearch#16726 http://support.elastic.co/hc/requests/17671 https://issues.liferay.com/browse/LPS-63269
- Loading branch information
1 parent
0348c49
commit 5acb4a7
Showing
5 changed files
with
350 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Bundle-SymbolicName: org.elasticsearch | ||
Bundle-Version: 2.2.0.LIFERAY-PATCHED-1 | ||
Export-Package: org.elasticsearch.* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import com.liferay.gradle.plugins.patcher.PatchTask | ||
|
||
apply plugin: "com.liferay.patcher" | ||
|
||
task patch(type: PatchTask) | ||
|
||
sourceCompatibility = "1.7" | ||
targetCompatibility = "1.7" | ||
|
||
dependencies { | ||
compile group: "org.elasticsearch", name: "elasticsearch", optional, transitive: false, version: "2.2.0" | ||
} | ||
|
||
patch { | ||
ext { | ||
autoClean = false | ||
} | ||
|
||
fileNames "org/elasticsearch/bootstrap/JarHell.java" | ||
|
||
originalLibModuleName = "elasticsearch" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<?xml version="1.0"?> | ||
<!DOCTYPE project> | ||
|
||
<project> | ||
<import file="../../build-module.xml" /> | ||
</project> |
98 changes: 98 additions & 0 deletions
98
modules/third-party/org-elasticsearch/patches/LPS-63269.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
diff --git a/org/elasticsearch/bootstrap/JarHell.java b/org/elasticsearch/bootstrap/JarHell.java | ||
index 3f77f6b..6b4a2a5 100644 | ||
--- a/org/elasticsearch/bootstrap/JarHell.java | ||
+++ b/org/elasticsearch/bootstrap/JarHell.java | ||
@@ -25,24 +25,13 @@ | ||
import org.elasticsearch.common.logging.ESLogger; | ||
import org.elasticsearch.common.logging.Loggers; | ||
|
||
-import java.io.IOException; | ||
import java.net.MalformedURLException; | ||
import java.net.URL; | ||
import java.net.URLClassLoader; | ||
-import java.nio.file.FileVisitResult; | ||
-import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
-import java.nio.file.SimpleFileVisitor; | ||
-import java.nio.file.attribute.BasicFileAttributes; | ||
import java.util.Arrays; | ||
-import java.util.Enumeration; | ||
-import java.util.HashMap; | ||
-import java.util.HashSet; | ||
import java.util.Locale; | ||
import java.util.Map; | ||
-import java.util.Set; | ||
-import java.util.jar.JarEntry; | ||
-import java.util.jar.JarFile; | ||
import java.util.jar.Manifest; | ||
|
||
/** | ||
@@ -150,62 +139,6 @@ | ||
*/ | ||
@SuppressForbidden(reason = "needs JarFile for speed, just reading entries") | ||
public static void checkJarHell(URL urls[]) throws Exception { | ||
- ESLogger logger = Loggers.getLogger(JarHell.class); | ||
- // we don't try to be sneaky and use deprecated/internal/not portable stuff | ||
- // like sun.boot.class.path, and with jigsaw we don't yet have a way to get | ||
- // a "list" at all. So just exclude any elements underneath the java home | ||
- String javaHome = System.getProperty("java.home"); | ||
- logger.debug("java.home: {}", javaHome); | ||
- final Map<String,Path> clazzes = new HashMap<>(32768); | ||
- Set<Path> seenJars = new HashSet<>(); | ||
- for (final URL url : urls) { | ||
- final Path path = PathUtils.get(url.toURI()); | ||
- // exclude system resources | ||
- if (path.startsWith(javaHome)) { | ||
- logger.debug("excluding system resource: {}", path); | ||
- continue; | ||
- } | ||
- if (path.toString().endsWith(".jar")) { | ||
- if (!seenJars.add(path)) { | ||
- logger.debug("excluding duplicate classpath element: {}", path); | ||
- continue; // we can't fail because of sheistiness with joda-time | ||
- } | ||
- logger.debug("examining jar: {}", path); | ||
- try (JarFile file = new JarFile(path.toString())) { | ||
- Manifest manifest = file.getManifest(); | ||
- if (manifest != null) { | ||
- checkManifest(manifest, path); | ||
- } | ||
- // inspect entries | ||
- Enumeration<JarEntry> elements = file.entries(); | ||
- while (elements.hasMoreElements()) { | ||
- String entry = elements.nextElement().getName(); | ||
- if (entry.endsWith(".class")) { | ||
- // for jar format, the separator is defined as / | ||
- entry = entry.replace('/', '.').substring(0, entry.length() - 6); | ||
- checkClass(clazzes, entry, path); | ||
- } | ||
- } | ||
- } | ||
- } else { | ||
- logger.debug("examining directory: {}", path); | ||
- // case for tests: where we have class files in the classpath | ||
- final Path root = PathUtils.get(url.toURI()); | ||
- final String sep = root.getFileSystem().getSeparator(); | ||
- Files.walkFileTree(root, new SimpleFileVisitor<Path>() { | ||
- @Override | ||
- public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { | ||
- String entry = root.relativize(file).toString(); | ||
- if (entry.endsWith(".class")) { | ||
- // normalize with the os separator | ||
- entry = entry.replace(sep, ".").substring(0, entry.length() - 6); | ||
- checkClass(clazzes, entry, path); | ||
- } | ||
- return super.visitFile(file, attrs); | ||
- } | ||
- }); | ||
- } | ||
- } | ||
} | ||
|
||
/** inspect manifest for sure incompatibilities */ | ||
@@ -285,3 +218,4 @@ | ||
} | ||
} | ||
} | ||
+/* @generated */ | ||
\ No newline at end of file |
221 changes: 221 additions & 0 deletions
221
modules/third-party/org-elasticsearch/src/main/java/org/elasticsearch/bootstrap/JarHell.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
/* | ||
* Licensed to Elasticsearch under one or more contributor | ||
* license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright | ||
* ownership. Elasticsearch 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. | ||
*/ | ||
|
||
package org.elasticsearch.bootstrap; | ||
|
||
import org.elasticsearch.Version; | ||
import org.elasticsearch.common.SuppressForbidden; | ||
import org.elasticsearch.common.io.PathUtils; | ||
import org.elasticsearch.common.logging.ESLogger; | ||
import org.elasticsearch.common.logging.Loggers; | ||
|
||
import java.net.MalformedURLException; | ||
import java.net.URL; | ||
import java.net.URLClassLoader; | ||
import java.nio.file.Path; | ||
import java.util.Arrays; | ||
import java.util.Locale; | ||
import java.util.Map; | ||
import java.util.jar.Manifest; | ||
|
||
/** | ||
* Simple check for duplicate class files across the classpath. | ||
* <p> | ||
* This class checks for incompatibilities in the following ways: | ||
* <ul> | ||
* <li>Checks that class files are not duplicated across jars.</li> | ||
* <li>Checks any {@code X-Compile-Target-JDK} value in the jar | ||
* manifest is compatible with current JRE</li> | ||
* <li>Checks any {@code X-Compile-Elasticsearch-Version} value in | ||
* the jar manifest is compatible with the current ES</li> | ||
* </ul> | ||
*/ | ||
public class JarHell { | ||
|
||
/** no instantiation */ | ||
private JarHell() {} | ||
|
||
/** Simple driver class, can be used eg. from builds. Returns non-zero on jar-hell */ | ||
@SuppressForbidden(reason = "command line tool") | ||
public static void main(String args[]) throws Exception { | ||
System.out.println("checking for jar hell..."); | ||
checkJarHell(); | ||
System.out.println("no jar hell found"); | ||
} | ||
|
||
/** | ||
* Checks the current classpath for duplicate classes | ||
* @throws IllegalStateException if jar hell was found | ||
*/ | ||
public static void checkJarHell() throws Exception { | ||
ClassLoader loader = JarHell.class.getClassLoader(); | ||
ESLogger logger = Loggers.getLogger(JarHell.class); | ||
if (logger.isDebugEnabled()) { | ||
logger.debug("java.class.path: {}", System.getProperty("java.class.path")); | ||
logger.debug("sun.boot.class.path: {}", System.getProperty("sun.boot.class.path")); | ||
if (loader instanceof URLClassLoader ) { | ||
logger.debug("classloader urls: {}", Arrays.toString(((URLClassLoader)loader).getURLs())); | ||
} | ||
} | ||
checkJarHell(parseClassPath()); | ||
} | ||
|
||
/** | ||
* Parses the classpath into an array of URLs | ||
* @return array of URLs | ||
* @throws IllegalStateException if the classpath contains empty elements | ||
*/ | ||
public static URL[] parseClassPath() { | ||
return parseClassPath(System.getProperty("java.class.path")); | ||
} | ||
|
||
/** | ||
* Parses the classpath into a set of URLs. For testing. | ||
* @param classPath classpath to parse (typically the system property {@code java.class.path}) | ||
* @return array of URLs | ||
* @throws IllegalStateException if the classpath contains empty elements | ||
*/ | ||
@SuppressForbidden(reason = "resolves against CWD because that is how classpaths work") | ||
static URL[] parseClassPath(String classPath) { | ||
String pathSeparator = System.getProperty("path.separator"); | ||
String fileSeparator = System.getProperty("file.separator"); | ||
String elements[] = classPath.split(pathSeparator); | ||
URL urlElements[] = new URL[elements.length]; | ||
for (int i = 0; i < elements.length; i++) { | ||
String element = elements[i]; | ||
// Technically empty classpath element behaves like CWD. | ||
// So below is the "correct" code, however in practice with ES, this is usually just a misconfiguration, | ||
// from old shell scripts left behind or something: | ||
// if (element.isEmpty()) { | ||
// element = System.getProperty("user.dir"); | ||
// } | ||
// Instead we just throw an exception, and keep it clean. | ||
if (element.isEmpty()) { | ||
throw new IllegalStateException("Classpath should not contain empty elements! (outdated shell script from a previous version?) classpath='" + classPath + "'"); | ||
} | ||
// we should be able to just Paths.get() each element, but unfortunately this is not the | ||
// whole story on how classpath parsing works: if you want to know, start at sun.misc.Launcher, | ||
// be sure to stop before you tear out your eyes. we just handle the "alternative" filename | ||
// specification which java seems to allow, explicitly, right here... | ||
if (element.startsWith("/") && "\\".equals(fileSeparator)) { | ||
// "correct" the entry to become a normal entry | ||
// change to correct file separators | ||
element = element.replace("/", "\\"); | ||
// if there is a drive letter, nuke the leading separator | ||
if (element.length() >= 3 && element.charAt(2) == ':') { | ||
element = element.substring(1); | ||
} | ||
} | ||
// now just parse as ordinary file | ||
try { | ||
urlElements[i] = PathUtils.get(element).toUri().toURL(); | ||
} catch (MalformedURLException e) { | ||
// should not happen, as we use the filesystem API | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
return urlElements; | ||
} | ||
|
||
/** | ||
* Checks the set of URLs for duplicate classes | ||
* @throws IllegalStateException if jar hell was found | ||
*/ | ||
@SuppressForbidden(reason = "needs JarFile for speed, just reading entries") | ||
public static void checkJarHell(URL urls[]) throws Exception { | ||
} | ||
|
||
/** inspect manifest for sure incompatibilities */ | ||
static void checkManifest(Manifest manifest, Path jar) { | ||
// give a nice error if jar requires a newer java version | ||
String targetVersion = manifest.getMainAttributes().getValue("X-Compile-Target-JDK"); | ||
if (targetVersion != null) { | ||
checkVersionFormat(targetVersion); | ||
checkJavaVersion(jar.toString(), targetVersion); | ||
} | ||
|
||
// give a nice error if jar is compiled against different es version | ||
String systemESVersion = Version.CURRENT.toString(); | ||
String targetESVersion = manifest.getMainAttributes().getValue("X-Compile-Elasticsearch-Version"); | ||
if (targetESVersion != null && targetESVersion.equals(systemESVersion) == false) { | ||
throw new IllegalStateException(jar + " requires Elasticsearch " + targetESVersion | ||
+ ", your system: " + systemESVersion); | ||
} | ||
} | ||
|
||
public static void checkVersionFormat(String targetVersion) { | ||
if (!JavaVersion.isValid(targetVersion)) { | ||
throw new IllegalStateException( | ||
String.format( | ||
Locale.ROOT, | ||
"version string must be a sequence of nonnegative decimal integers separated by \".\"'s and may have leading zeros but was %s", | ||
targetVersion | ||
) | ||
); | ||
} | ||
} | ||
|
||
/** | ||
* Checks that the java specification version {@code targetVersion} | ||
* required by {@code resource} is compatible with the current installation. | ||
*/ | ||
public static void checkJavaVersion(String resource, String targetVersion) { | ||
JavaVersion version = JavaVersion.parse(targetVersion); | ||
if (JavaVersion.current().compareTo(version) < 0) { | ||
throw new IllegalStateException( | ||
String.format( | ||
Locale.ROOT, | ||
"%s requires Java %s:, your system: %s", | ||
resource, | ||
targetVersion, | ||
JavaVersion.current().toString() | ||
) | ||
); | ||
} | ||
} | ||
|
||
static void checkClass(Map<String,Path> clazzes, String clazz, Path jarpath) { | ||
Path previous = clazzes.put(clazz, jarpath); | ||
if (previous != null) { | ||
if (previous.equals(jarpath)) { | ||
if (clazz.startsWith("org.apache.xmlbeans")) { | ||
return; // https://issues.apache.org/jira/browse/XMLBEANS-499 | ||
} | ||
// throw a better exception in this ridiculous case. | ||
// unfortunately the zip file format allows this buggy possibility | ||
// UweSays: It can, but should be considered as bug :-) | ||
throw new IllegalStateException("jar hell!" + System.lineSeparator() + | ||
"class: " + clazz + System.lineSeparator() + | ||
"exists multiple times in jar: " + jarpath + " !!!!!!!!!"); | ||
} else { | ||
if (clazz.startsWith("org.apache.log4j")) { | ||
return; // go figure, jar hell for what should be System.out.println... | ||
} | ||
if (clazz.equals("org.joda.time.base.BaseDateTime")) { | ||
return; // apparently this is intentional... clean this up | ||
} | ||
throw new IllegalStateException("jar hell!" + System.lineSeparator() + | ||
"class: " + clazz + System.lineSeparator() + | ||
"jar1: " + previous + System.lineSeparator() + | ||
"jar2: " + jarpath); | ||
} | ||
} | ||
} | ||
} | ||
/* @generated */ |