Skip to content

Commit

Permalink
Merge pull request #10 from deathcap/guava10
Browse files Browse the repository at this point in the history
Relocate Guava at build and runtime for backward compatibility. Closes GH-5
  • Loading branch information
deathcap committed Apr 27, 2015
2 parents d36872c + 32994f3 commit 488ecbe
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 5 deletions.
14 changes: 14 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@
<artifactId>configurate-hocon</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>5.0.3</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
<version>5.0.3</version>
</dependency>
</dependencies>

<build>
Expand Down Expand Up @@ -87,6 +97,10 @@
</excludes>
</artifactSet>
<relocations>
<relocation>
<pattern>com.google.common</pattern>
<shadedPattern>io.github.deathcap.bukkit2sponge.libs.guava17.com.google.common</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@

// based on http://svn.apache.org/viewvc/maven/plugins/tags/maven-shade-plugin-2.3/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java?revision=1590870&view=markup
// and http://svn.apache.org/viewvc/maven/plugins/tags/maven-shade-plugin-2.3/src/main/java/org/apache/maven/plugins/shade/relocation/SimpleRelocator.java?revision=1590870&view=co

package io.github.deathcap.bukkit2sponge.plugin;


/*
* 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.
*/


import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.commons.Remapper;
import org.objectweb.asm.commons.RemappingClassAdapter;


public class DefaultShader
{
private RelocatorRemapper remapper;


public DefaultShader( String inPrefix, String outPrefix) throws IOException
{
this.remapper = new RelocatorRemapper(inPrefix, outPrefix);

}

public byte[] getRemappedClass( byte[] is)
throws IOException
{

ClassReader cr = new ClassReader( is );
ClassWriter cw = new ClassWriter( cr, 0 );

ClassVisitor cv = new RemappingClassAdapter( cw, remapper )
{
/*
@Override
public void visitSource( final String source, final String debug )
{
if ( source == null )
{
super.visitSource( source, debug );
}
else
{
final String fqSource = pkg + source;
final String mappedSource = remapper.map( fqSource );
final String filename = mappedSource.substring( mappedSource.lastIndexOf( '/' ) + 1 );
super.visitSource( filename, debug );
}
}
*/
};

try
{
cr.accept( cv, ClassReader.EXPAND_FRAMES );
}
catch ( Throwable ise )
{
throw new RuntimeException( ise ) ;
}

byte[] renamedClass = cw.toByteArray();

return renamedClass;
}

/**
* @author Jason van Zyl
*/

public class RelocatorRemapper
extends Remapper
{

private final Pattern classPattern = Pattern.compile( "(\\[*)?L(.+);" );
private final String inPrefix;
private final String outPrefix;

public RelocatorRemapper(String inPrefix, String outPrefix)
{
this.inPrefix = inPrefix;
this.outPrefix = outPrefix;
}

@Override
public Object mapValue( Object object )
{
if ( object instanceof String )
{
String name = (String) object;
String value = name;

String prefix = "";
String suffix = "";

Matcher m = classPattern.matcher( name );
if ( m.matches() )
{
prefix = m.group( 1 ) + "L";
suffix = ";";
name = m.group( 2 );
}

value = map( name );

return value;
}

return super.mapValue( object );
}

@Override
public String map( String name )
{
if (name.startsWith(inPrefix)) {
return outPrefix + name;
} else {
return name;
}
}

}

}

Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

Expand Down Expand Up @@ -49,10 +50,10 @@ public List<PluginContainer> loadPlugins(List<URL> urls) {
}

private boolean loadJar(List<PluginContainer> result, URLClassLoader root, URL url) {
URLClassLoader classLoader = new URLClassLoader(new URL[]{url}, root);
boolean hasPlugin = false;

try (InputStream fileIn = url.openStream();
try (ShinyClassLoader classLoader = new ShinyClassLoader(new URL[]{url}, root);
InputStream fileIn = url.openStream();
ZipInputStream zipIn = new ZipInputStream(fileIn)
) {
ZipEntry entryIn;
Expand All @@ -63,9 +64,9 @@ private boolean loadJar(List<PluginContainer> result, URLClassLoader root, URL u

Class<?> clazz;
try {
clazz = classLoader.loadClass(name);
clazz = classLoader.findClass(name);
} catch (Throwable t) {
Bukkit2Sponge.instance.getLogger().warning("Error loading " + url.getFile() + "/" + name + t);
Bukkit2Sponge.instance.getLogger().log(Level.WARNING, "Error loading " + url.getFile() + "/" + name, t);
continue;
}

Expand All @@ -91,7 +92,7 @@ private PluginContainer fromClass(Class<?> clazz) {
ShinyPluginContainer container = new ShinyPluginContainer(clazz);
return container;
} catch (Throwable t) {
Bukkit2Sponge.instance.getLogger().warning("Error initializing " + annotation.id() + " (" + clazz + ")" + t);
Bukkit2Sponge.instance.getLogger().log(Level.WARNING, "Error initializing " + annotation.id() + " (" + clazz + ")", t);
}
}
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package io.github.deathcap.bukkit2sponge.plugin;

import com.google.common.io.ByteStreams;

import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSigner;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.List;

public class ShinyClassLoader extends URLClassLoader {

private static DefaultShader shader = null;

private final static String com_google_common = new String(new char[] {'c','o','m','/','g','o','o','g','l','e','/','c','o','m','m','o','n',});
private final static String inPrefix = com_google_common;
private final static String outPrefix = "io/github/deathcap/bukkit2sponge/libs/guava17/";

static {
try {
shader = new DefaultShader(inPrefix, outPrefix);
} catch (IOException ex) {
ex.printStackTrace();
}
}

public ShinyClassLoader(URL[] urls, ClassLoader parent) throws IOException {
super(urls, parent);
}

private byte[] relocateBytecode(byte[] bytes) throws IOException {
return shader.getRemappedClass(bytes);
}

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String path = name.replace('.', '/').concat(".class");
URL url = this.findResource(path);
if (url == null) {
throw new ClassNotFoundException("Unable to find class resource " + name + " at " + path);
}

InputStream stream = null;
byte[] bytecode = null;
try {
stream = url.openStream();
bytecode = ByteStreams.toByteArray(stream);
} catch (IOException ex) {
ex.printStackTrace();
throw new ClassNotFoundException("Unable to open or read stream for class " + name + ": " + ex.getLocalizedMessage());
}

CodeSource codeSource = null;
try {
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
URL jarURL = jarURLConnection.getJarFileURL();
codeSource = new CodeSource(jarURL, new CodeSigner[0]);
} catch (IOException ex) {
ex.printStackTrace();
throw new ClassNotFoundException("Unable to read code source for class " + name + ": " + ex.getLocalizedMessage());
}

byte[] remappedBytecode;
try {
remappedBytecode = relocateBytecode(bytecode);
} catch (IOException ex) {
ex.printStackTrace();
//throw new ClassNotFoundException("Unable to relocate class " + name + ": " + ex.getLocalizedMessage());
remappedBytecode = bytecode;
}

Class<?> result = this.defineClass(name, remappedBytecode, 0, remappedBytecode.length, codeSource);
if (result != null) {
this.resolveClass(result);
}

return result;
}
}

0 comments on commit 488ecbe

Please sign in to comment.