Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fail startup (and tests) on jar hell #11932

Merged
merged 3 commits into from
Jun 30, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ public void run() {
// install any plugins into classpath
setupPlugins(environment);

// look for jar hell
JarHell.checkJarHell();

// install SM after natives, shutdown hooks, etc.
setupSecurity(settings, environment);

Expand Down Expand Up @@ -375,7 +378,8 @@ private static void setupPlugins(Environment environment) throws IOException {
return;
}

ClassLoader classLoader = ClassLoader.getSystemClassLoader();
// note: there's only one classloader here, but Uwe gets upset otherwise.
ClassLoader classLoader = Bootstrap.class.getClassLoader();
Class<?> classLoaderClass = classLoader.getClass();
Method addURL = null;
while (!classLoaderClass.equals(Object.class)) {
Expand Down
109 changes: 109 additions & 0 deletions core/src/main/java/org/elasticsearch/bootstrap/JarHell.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* 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.common.SuppressForbidden;
import org.elasticsearch.common.io.PathUtils;

import java.io.IOException;
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.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/** Simple check for duplicate class files across the classpath */
class JarHell {

/**
* Checks the current classloader for duplicate classes
* @throws IllegalStateException if jar hell was found
*/
@SuppressForbidden(reason = "needs JarFile for speed, just reading entries")
static void checkJarHell() throws Exception {
ClassLoader loader = JarHell.class.getClassLoader();
if (loader instanceof URLClassLoader == false) {
return;
}
final Map<String,URL> clazzes = new HashMap<>(32768);
Set<String> seenJars = new HashSet<>();
for (final URL url : ((URLClassLoader)loader).getURLs()) {
String path = url.getPath();
if (path.endsWith(".jar")) {
if (!seenJars.add(path)) {
continue; // we can't fail because of sheistiness with joda-time
}
try (JarFile file = new JarFile(url.getPath())) {
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, url);
}
}
}
} else {
// 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, url);
}
return super.visitFile(file, attrs);
}
});
}
}
}

@SuppressForbidden(reason = "proper use of URL to reduce noise")
static void checkClass(Map<String,URL> clazzes, String clazz, URL url) {
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
}
URL previous = clazzes.put(clazz, url);
if (previous != null) {
throw new IllegalStateException("jar hell!" + System.lineSeparator() +
"class: " + clazz + System.lineSeparator() +
"jar1: " + previous.getPath() + System.lineSeparator() +
"jar2: " + url.getPath());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ static void configure(Environment environment) throws Exception {
*/
@SuppressForbidden(reason = "proper use of URL")
static void setCodebaseProperties() {
ClassLoader loader = ClassLoader.getSystemClassLoader();
ClassLoader loader = Security.class.getClassLoader();
if (loader instanceof URLClassLoader) {
for (URL url : ((URLClassLoader)loader).getURLs()) {
for (Map.Entry<Pattern,String> e : SPECIAL_JARS.entrySet()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

package org.elasticsearch.action.bulk;

import com.carrotsearch.ant.tasks.junit4.dependencies.com.carrotsearch.randomizedtesting.generators.RandomPicks;
import com.carrotsearch.randomizedtesting.generators.RandomPicks;

import org.elasticsearch.action.get.MultiGetItemResponse;
import org.elasticsearch.action.get.MultiGetRequestBuilder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ public class BootstrapForTesting {
static {
// just like bootstrap, initialize natives, then SM
Bootstrap.initializeNatives(true, true, true);

// check for jar hell
try {
JarHell.checkJarHell();
} catch (Exception e) {
throw new RuntimeException("found jar hell in test classpath", e);
}

// make sure java.io.tmpdir exists always (in case code uses it in a static initializer)
Path javaTmpDir = PathUtils.get(Objects.requireNonNull(System.getProperty("java.io.tmpdir"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@

package org.elasticsearch.client.node;

import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet;

import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.GenericAction;
import org.elasticsearch.action.support.ActionFilter;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.TransportAction;
import org.elasticsearch.client.AbstractClientHeadersTests;
Expand All @@ -31,14 +33,15 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.threadpool.ThreadPool;

import java.util.Collections;
import java.util.HashMap;

/**
*
*/
public class NodeClientHeadersTests extends AbstractClientHeadersTests {

private static final ActionFilters EMPTY_FILTERS = new ActionFilters(ImmutableSet.of());
private static final ActionFilters EMPTY_FILTERS = new ActionFilters(Collections.<ActionFilter>emptySet());

@Override
protected Client buildClient(Settings headersSettings, GenericAction[] testedActions) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

package org.elasticsearch.common.collect;

import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap;
import org.elasticsearch.test.ElasticsearchTestCase;

import java.util.HashMap;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@

package org.elasticsearch.common.lucene.index;

import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.Lists;
import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.Maps;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import org.apache.lucene.analysis.core.KeywordAnalyzer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
*/
package org.elasticsearch.index.store;

import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.Lists;
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
import com.google.common.base.Charsets;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;

import org.apache.lucene.codecs.CodecUtil;
import org.apache.lucene.index.CheckIndex;
import org.apache.lucene.index.IndexFileNames;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

package org.elasticsearch.index.store;

import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.Lists;
import com.google.common.collect.Lists;
import com.carrotsearch.randomizedtesting.generators.RandomPicks;

import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

package org.elasticsearch.test;

import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.Lists;
import com.google.common.collect.Lists;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

package org.elasticsearch.transport;

import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.ImmutableList;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.elasticsearch.action.*;
Expand Down Expand Up @@ -380,7 +379,9 @@ public String description() {

@Override
public Collection<Class<? extends Module>> modules() {
return ImmutableList.of(ActionLoggingModule.class);
Collection<Class<? extends Module>> classes = new ArrayList<>();
classes.add(ActionLoggingModule.class);
return classes;
}
}

Expand Down
1 change: 0 additions & 1 deletion plugins/cloud-azure/licenses/stax-api-1.0.1.jar.sha1

This file was deleted.

6 changes: 6 additions & 0 deletions plugins/cloud-azure/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ governing permissions and limitations under the License. -->
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-management-compute</artifactId>
<version>0.7.0</version>
<exclusions>
<exclusion>
<groupId>stax</groupId>
<artifactId>stax-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
Expand Down
Loading