package act.boot.server; /*- * #%L * ACT Framework * %% * Copyright (C) 2014 - 2017 ActFramework * %% * 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. * #L% */ import act.Act; import act.asm.ClassReader; import act.asm.ClassWriter; import act.boot.PluginClassProvider; import act.boot.app.FullStackAppBootstrapClassLoader; import act.util.ByteCodeVisitor; import act.util.Jars; import org.osgl.$; import org.osgl.logging.L; import org.osgl.logging.Logger; import org.osgl.util.C; import org.osgl.util.E; import java.io.File; import java.lang.reflect.Modifier; import java.util.List; import java.util.Map; import java.util.Set; import static act.Constants.*; /** * This class loader is responsible for loading Act classes */ public class ServerBootstrapClassLoader extends ClassLoader implements PluginClassProvider { private static Logger logger = L.get(ServerBootstrapClassLoader.class); protected final Class<?> PLUGIN_CLASS; private File lib; private File plugin; private Map<String, byte[]> libBC = C.newMap(); private Map<String, byte[]> pluginBC = C.newMap(); private List<Class<?>> pluginClasses = C.newList(); public ServerBootstrapClassLoader(ClassLoader parent) { super(parent); preload(); PLUGIN_CLASS = $.classForName("act.plugin.Plugin", this); } public ServerBootstrapClassLoader() { this(_getParent()); } private static ClassLoader _getParent() { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (null == cl) { cl = ClassLoader.getSystemClassLoader(); } return cl; } protected void preload() { String actHome = System.getProperty(ACT_HOME); if (null == actHome) { actHome = System.getenv(ACT_HOME); } if (null == actHome) { actHome = guessHome(); } File home = new File(actHome); lib = new File(home, "lib"); plugin = new File(home, "plugin"); verifyHome(); buildIndex(); } private String guessHome() { return new File(".").getAbsolutePath(); } private void verifyHome() { if (!verifyDir(lib) || !verifyDir(plugin)) { throw E.unexpected("Cannot load Act: can't find lib or plugin dir"); } } private boolean verifyDir(File dir) { return dir.exists() && dir.canExecute() && dir.isDirectory(); } private void buildIndex() { libBC.putAll(Jars.buildClassNameIndex(lib)); pluginBC.putAll(Jars.buildClassNameIndex(plugin)); File actJar = Jars.probeJarFile(Act.class); if (null == actJar) { logger.warn("Cannot find jar file for Act"); } else { pluginBC.putAll(Jars.buildClassNameIndex(C.list(actJar))); } } @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { Class<?> c = findLoadedClass(name); if (c != null) { return c; } if (!protectedClasses.contains(name)) { c = loadActClass(name, resolve, false); } if (null == c) { return super.loadClass(name, resolve); } else { return c; } } public List<Class<?>> pluginClasses() { if (pluginClasses.isEmpty()) { for (String className : C.list(pluginBC.keySet())) { Class<?> c = loadActClass(className, true, true); assert null != c; int modifier = c.getModifiers(); if (Modifier.isAbstract(modifier) || !Modifier.isPublic(modifier) || c.isInterface()) { continue; } if (PLUGIN_CLASS.isAssignableFrom(c)) { pluginClasses.add(c); } } } return C.list(pluginClasses); } protected byte[] tryLoadResource(String name) { return null; } protected Class<?> loadActClass(String name, boolean resolve, boolean pluginOnly) { boolean fromPlugin = false; byte[] ba = pluginBC.remove(name); if (null == ba) { if (!pluginOnly) { ba = libBC.remove(name); } } else { fromPlugin = true; } if (null == ba) { ba = tryLoadResource(name); } if (null == ba) { if (pluginOnly) { return findLoadedClass(name); } return null; } Class<?> c = null; if (!name.startsWith(ACT_PKG) || name.startsWith(ASM_PKG)) { // skip bytecode enhancement for asm classes or non Act classes c = super.defineClass(name, ba, 0, ba.length, DOMAIN); } if (null == c) { $.Var<ClassWriter> cw = $.val(null); ByteCodeVisitor enhancer = Act.enhancerManager().generalEnhancer(name, cw); if (null == enhancer) { c = super.defineClass(name, ba, 0, ba.length, DOMAIN); } else { ClassWriter w = new ClassWriter(ClassWriter.COMPUTE_FRAMES); cw.set(w); enhancer.commitDownstream(); ClassReader r; r = new ClassReader(ba); try { r.accept(enhancer, 0); byte[] baNew = w.toByteArray(); c = super.defineClass(name, baNew, 0, baNew.length, DOMAIN); } catch (RuntimeException e) { throw e; } catch (Error e) { throw e; } catch (Exception e) { throw E.unexpected("Error processing class " + name); } } } if (resolve) { super.resolveClass(c); } return c; } public Class<?> createClass(String name, byte[] b) throws ClassFormatError { return super.defineClass(name, b, 0, b.length, DOMAIN); } private static java.security.ProtectionDomain DOMAIN; static { DOMAIN = (java.security.ProtectionDomain) java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run() { return ServerBootstrapClassLoader.class.getProtectionDomain(); } }); } private static final Set<String> protectedClasses = C.set( ServerBootstrapClassLoader.class.getName(), FullStackAppBootstrapClassLoader.class.getName(), PluginClassProvider.class.getName() //Plugin.class.getName(), //ClassFilter.class.getName() ); }