/*
* JBoss, Home of Professional Open Source
* Copyright 2012, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed 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.jboss.arquillian.warp.impl.client.transformation;
import java.io.ByteArrayInputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import javassist.ClassPool;
import javassist.CtClass;
import org.jboss.arquillian.warp.Inspection;
import org.jboss.arquillian.warp.impl.client.separation.SeparateInvocator;
import org.jboss.arquillian.warp.impl.client.separation.SeparatedClassLoader;
import org.jboss.arquillian.warp.impl.utils.ClassLoaderUtils;
import org.jboss.arquillian.warp.impl.utils.SerializationUtils;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.NamedAsset;
import org.jboss.shrinkwrap.api.classloader.ShrinkWrapClassLoader;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
/**
* <p>
* Takes {@link TransformedInspection} and renames it to original name, providing bytecode of transformed and renamed class.
* </p>
* <p>
* <p>
* In order to allow renaming transformed class to the name of original class, {@link MigratedInspection} uses
* {@link SeparatedClassLoader} internally.
* </p>
*
* @author Lukas Fryc
*/
public class MigratedInspection {
private TransformedInspection transformed;
private MigrationResult migrated;
public MigratedInspection(TransformedInspection transformed) throws InspectionTransformationException {
this.transformed = transformed;
this.migrated = migrate();
}
private MigrationResult migrate() throws InspectionTransformationException {
byte[] oldClassFile = transformed.toBytecode();
String oldClassName = transformed.getTransformedClass().getName();
String newClassName = transformed.getOriginalClass().getName();
Inspection serverInspection = transformed.getTransformedInspection();
NamedAsset transformedAsset = transformed.toShrinkWrapAsset();
try {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
JavaArchive archive = ShrinkWrap.create(JavaArchive.class).add(transformedAsset);
ShrinkWrapClassLoader shrinkWrapClassLoader =
new ShrinkWrapClassLoader(ClassLoaderUtils.getBootstrapClassLoader(),
archive);
SeparatedClassLoader separatedClassLoader =
new SeparatedClassLoader(shrinkWrapClassLoader, contextClassLoader);
return SeparateInvocator.<Migration, MigrationImpl>invoke(MigrationImpl.class, separatedClassLoader).process(
oldClassName, newClassName, oldClassFile, serverInspection);
} catch (Throwable e) {
throw new IllegalStateException("Cannot migrate transformed inspection back to original name", e);
}
}
public byte[] toBytecode() {
return migrated.bytecode;
}
public byte[] toSerializedForm() {
return migrated.serializedMigratedInspection;
}
public interface Migration {
MigrationResult process(String oldClassName, String newClassName, byte[] oldClassFile,
Inspection transformedInspection) throws Exception;
}
public static class MigrationImpl implements Migration {
@Override
public MigrationResult process(String oldClassName, String newClassName, byte[] oldClassFile,
Inspection transformedInspection) throws Exception {
MigrationResult result = new MigrationResult();
ClassPool pool = new ClassPool();
CtClass clazz = pool.makeClassIfNew(new ByteArrayInputStream(oldClassFile));
clazz.replaceClassName(oldClassName, newClassName);
@SuppressWarnings("unchecked")
Class<Serializable> migratedClass = (Class<Serializable>) clazz.toClass();
result.bytecode = clazz.toBytecode();
try {
Class<?> oldClass = transformedInspection.getClass();
Serializable migratedInspection = migratedClass.newInstance();
for (Field newF : migratedClass.getDeclaredFields()) {
if (java.lang.reflect.Modifier.isStatic(newF.getModifiers())
&& java.lang.reflect.Modifier.isFinal(newF.getModifiers())) {
continue;
}
Field oldF = oldClass.getDeclaredField(newF.getName());
oldF.setAccessible(true);
newF.setAccessible(true);
newF.set(migratedInspection, oldF.get(transformedInspection));
}
result.serializedMigratedInspection = SerializationUtils.serializeToBytes(migratedInspection);
} catch (Exception e) {
throw new IllegalStateException("Unable to clone " + transformedInspection.getClass().getName()
+ " to migrated class " + migratedClass.getName(), e);
}
return result;
}
}
public static class MigrationResult implements Serializable {
private static final long serialVersionUID = 1L;
public byte[] bytecode;
public byte[] serializedMigratedInspection;
}
}