org.drools.android.MultiDexClassLoader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of drools-android Show documentation
Show all versions of drools-android Show documentation
Integration code that enables Drools to run on the Android platform
/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* 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.drools.android;
import com.android.dex.DexFormat;
import com.android.dx.cf.direct.DirectClassFile;
import com.android.dx.cf.direct.StdAttributeFactory;
import com.android.dx.dex.DexOptions;
import com.android.dx.dex.cf.CfOptions;
import com.android.dx.dex.cf.CfTranslator;
import com.android.dx.dex.file.DexFile;
import dalvik.system.BaseDexClassLoader;
import dalvik.system.DexClassLoader;
import org.drools.core.util.ByteArrayClassLoader;
import org.mvel2.util.MVELClassLoader;
import org.robolectric.annotation.internal.Instrument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.security.ProtectionDomain;
import static org.drools.android.DroolsAndroidContext.*;
/**
* Maintains separate dex files for each defined class.
*/
@Instrument
public class MultiDexClassLoader extends DexClassLoader implements ByteArrayClassLoader, MVELClassLoader {
private static final Logger log = LoggerFactory.getLogger(MultiDexClassLoader.class);
private String dexPath="";
final DexOptions dex_options = new DexOptions();
final CfOptions cf_options = new CfOptions();
private static Field pathListField;
private static Class dexPathListClazz;
private static Constructor dexPathListConstructor;
static {
try {
dexPathListClazz = Class.forName("dalvik.system.DexPathList");
dexPathListConstructor = dexPathListClazz.getConstructor(ClassLoader.class, String.class, String.class, File.class);
pathListField = BaseDexClassLoader.class.getDeclaredField("pathList");
pathListField.setAccessible(true);
} catch (Exception e) {
log.error("Reflection error", e);
}
}
public MultiDexClassLoader(ClassLoader parent) {
super(new File(getDexDir(), "temp.dex").getAbsolutePath(),
getOptimizedDir().getAbsolutePath(),
getContext().getApplicationInfo().nativeLibraryDir,
parent!=null ? parent : getContext().getClassLoader());
dex_options.targetApiLevel = DexFormat.API_NO_EXTENDED_OPCODES;
cf_options.optimize = true;
}
/**
* Convert class to dex
* @param name class name
* @param bytes classfile bytes
* @return the dex file name
*/
private String writeClass(String name, byte[] bytes) throws IOException {
name = name.replace('.','/');
File dexFile = new File(String.format("%s/%s.dex", getDexDir(), name));
if(dexFile.exists() && isReuseClassFiles()) {
if (log.isTraceEnabled())
log.trace(String.format("Reused class [%s] from cache: %s", name, dexFile.getAbsolutePath()));
return dexFile.getAbsolutePath();
}
FileOutputStream fos = null;
try {
//convert .class ==> .dex
DexFile file = new DexFile(dex_options);
DirectClassFile cf =
new DirectClassFile(bytes, name + ".class", cf_options.strictNameCheck);
cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
cf.getMagic();
file.add(CfTranslator.translate(cf, bytes, cf_options, dex_options, file));
//write dex file to cache dir
dexFile.getParentFile().mkdirs();
if (dexFile.exists()) dexFile.delete();
fos = new FileOutputStream(dexFile);
file.writeTo(fos, null, false);
if (log.isTraceEnabled())
log.trace(String.format("Wrote class [%s] to cache: %s", name, dexFile.getAbsolutePath()));
return dexFile.getAbsolutePath();
} finally {
if(fos!=null) {
try {
fos.close();
} catch (IOException e) {}
}
}
}
public Class defineClass(String name, byte[] bytes) {
try {
String path = writeClass(name, bytes);
dexPath += (dexPath.isEmpty() ? "" : ":") + path;
log.trace("New Dexpath: " + dexPath);
pathListField.set(this, dexPathListConstructor.newInstance(this,
path,
getContext().getApplicationInfo().nativeLibraryDir,
getOptimizedDir()));
return findClass(name);
} catch (Exception e) {
log.error("Error", e);
throw new RuntimeException(e);
}
}
@Override
public Class< ? > defineClass(final String name,
final byte[] bytes,
final ProtectionDomain domain) {
return defineClass(name, bytes);
}
@Override
public Class defineClassX(String className, byte[] b, int start, int len) {
byte[] newBytes = new byte[len];
System.arraycopy(b, start, newBytes, 0, len);
return defineClass(className, newBytes);
}
@Override
public String toString() {
try {
return getClass().getName() + "[" + pathListField.get(this) + "]";
} catch (IllegalAccessException e) {
return super.toString();
}
}
}