Java tutorial
/** * personium.io * Copyright 2014 FUJITSU LIMITED * * 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 com.fujitsu.dc.engine.extension.support; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fujitsu.dc.engine.DcEngineException; /** * Extension? jar??Rhino???. */ public class ExtensionJarLoader { /** . */ private static Logger log = LoggerFactory.getLogger(ExtensionJarLoader.class); private static ExtensionJarLoader singleton = null; /** Extension jar??????. * ?????"/dc-engine/extensions"?? */ public static final String ENGINE_EXTENSION_DIR_KEY = "com.fujitsu.dc.environment"; /** ?????????? Extension jar??. */ public static final String DEFAULT_EXTENSION_DIR = "/fj"; private static final String JAR_SUFFIX = "jar"; // ? private Path extensionJarDirectory = null; // ExtensionJarDirectory??/? jar????? private boolean searchDescendant = true; // jar????????? private ClassLoader classloader = null; /** * JavaScript?????Java?Set. */ private Set<Class<? extends Scriptable>> scriptableClassSet = null; /** * ?Extensionjar????. * @param parentCl * @param filter Extension * @return ExtensionLoader * @throws IOException Extension???? * @throws DcEngineException Extension???? */ public static ExtensionJarLoader getInstance(ClassLoader parentCl, ExtensionClassFilter filter) throws IOException, DcEngineException { if (null == singleton) { String extensionDir = System.getProperty(ENGINE_EXTENSION_DIR_KEY, DEFAULT_EXTENSION_DIR) + "/dc-engine/extensions"; singleton = new ExtensionJarLoader(Paths.get(new File(extensionDir).toURI()), false, parentCl, filter); } return singleton; } /** * . * PCS????????????????? getInstance()??? * @param extJarDir Extensionjar?? * @param searchDescend true: ?, false: ???? * @param parentCl * @param filter Extension * @throws IOException Extension???? * @throws DcEngineException */ private ExtensionJarLoader(Path extJarDir, boolean searchDescend, ClassLoader parentCl, ExtensionClassFilter filter) throws IOException, DcEngineException { extensionJarDirectory = extJarDir; searchDescendant = searchDescend; List<URL> jarPaths = getJarPaths(extensionJarDirectory, searchDescendant); classloader = new URLClassLoader(jarPaths.toArray(new URL[] {}), parentCl); scriptableClassSet = loadPrototypeClassSet(jarPaths, filter); } /** * Extension? jar?????. * @return */ public ClassLoader getClassLoader() { return classloader; } /** * JavaScript?????Java?Set??. * @return ????Java?Set */ public Set<Class<? extends Scriptable>> getPrototypeClassSet() { return scriptableClassSet; } /** * ExtensionJarDirectory?? jar?URL??. * ???? "jar"???? * @param extJarDir Extensionjar?? * @param searchDescend true: ?, false: ???? * @return jar? URL. */ private List<URL> getJarPaths(Path extJarDir, boolean searchDescend) throws DcEngineException { try { // ?? List<URL> uriList = new ArrayList<URL>(); // jar????? uriList.add(new URL("file", "", extJarDir.toFile().getAbsolutePath() + "/")); // jar? File[] jarFiles = extJarDir.toFile().listFiles(new FileFilter() { @Override public boolean accept(File pathname) { if (!pathname.exists() || !pathname.canRead() || pathname.isDirectory()) { return false; } return FilenameUtils.isExtension(pathname.getName(), JAR_SUFFIX); } }); if (null != jarFiles) { for (File jarFile : jarFiles) { try { uriList.add(new URL("file", "", jarFile.getCanonicalPath())); log.info(String.format("Info: Adding extension jar file %s to classloader.", jarFile.toURI())); } catch (MalformedURLException e) { // ############################################################################3 // ????????? jar???????? jar???? // ? Extension????????? Extension????? UserScript?? // ???????????? // ?? Extension?????Script??? // ############################################################################3 log.info(String.format("Warn: Some Extension jar file has malformed path. Ignoring: %s", jarFile.toURI())); } catch (IOException e) { log.info(String.format("Warn: Some Extension jar file has malformed path. Ignoring: %s", jarFile.toURI())); } } } // ? File[] subDirs = extJarDir.toFile().listFiles(new FileFilter() { @Override public boolean accept(File pathname) { return pathname.exists() && pathname.isDirectory() && pathname.canRead(); } }); if (null != subDirs) { for (File subDir : subDirs) { // jar? uriList.addAll(getJarPaths(subDir.toPath(), searchDescend)); } } return uriList; } catch (RuntimeException e) { e.printStackTrace(); log.info(String.format("Warn: Error occured while loading Extension: %s", e.getMessage())); throw new DcEngineException("Error occured while loading Extension.", DcEngineException.STATUSCODE_SERVER_ERROR, e); } catch (Exception e) { log.info(String.format("Warn: Error occured while loading Extension: %s", e.getMessage())); throw new DcEngineException("Error occured while loading Extension.", DcEngineException.STATUSCODE_SERVER_ERROR, e); } } /** * jar???JavaScript??? Set??. * @param jarPaths jar? * @param filter Extension * @return Javascript??????Java? * @throws IOException ?????/???? */ @SuppressWarnings("unchecked") private Set<Class<? extends Scriptable>> loadPrototypeClassSet(List<URL> jarPaths, ExtensionClassFilter filter) throws IOException, DcEngineException { scriptableClassSet = new HashSet<Class<? extends Scriptable>>(); for (URL jarUrl : jarPaths) { JarFile jar = null; try { File jarFile = new File(jarUrl.getPath()); if (jarFile.isDirectory()) { continue; } jar = new JarFile(jarFile); for (Enumeration<JarEntry> ent = jar.entries(); ent.hasMoreElements();) { JarEntry entry = ent.nextElement(); String[] pathAndName = resolveJarEntry(entry.getName()); if (null == pathAndName) { continue; } String entryPath = pathAndName[0]; String entryName = pathAndName[1]; if (null == entryPath || null == entryName) { continue; } // jar??"/" ???????? entryPath = entryPath.replaceAll("\\/", "\\."); // ??? JavaScript????? filter?????? if (filter.accept(entryPath, entryName)) { String className = entryPath + "." + entryName; try { Class<?> cl = classloader.loadClass(className); if (ScriptableObject.class.isAssignableFrom(cl) || Scriptable.class.isAssignableFrom(cl)) { scriptableClassSet.add((Class<? extends Scriptable>) cl); log.info(String.format("Info: Extension class %s is revealed to JavaScript.", className)); // OK. continue; } // ScriptableObject/Scriptable????????JavaScript?????? log.info(String.format("Info: Extension class %s is not derived from " + "ScriptableObject class or does not implment Scriptable interface. Ignored.", className)); } catch (ClassNotFoundException e) { log.warn(String.format("Warn: Extension class %s is not found in classLoader: %s", className, e.getMessage()), e); } catch (NoClassDefFoundError e) { log.warn(String.format("Warn: Extension class %s is not found in classLoader: %s", className, e.getMessage()), e); } catch (Exception e) { log.warn(String.format("Warn: Extension class %s cannot be loaded into classLoader: %s " + "or the jar content is invalid.", className, e.getMessage()), e); } } } } catch (RuntimeException e) { log.warn(String.format("Warn: Failed to handle Extension jar file %s: %s", jarUrl.toString(), e.getMessage()), e); } catch (Exception e) { log.warn(String.format("Warn: Failed to handle Extension jar file %s: %s", jarUrl.toString(), e.getMessage()), e); continue; } finally { IOUtils.closeQuietly(jar); } } return scriptableClassSet; } /** * Jar????????. * @param jarEntryStr Jar? * @return ? ????????? */ private String[] resolveJarEntry(String jarEntryStr) { if (null == jarEntryStr || jarEntryStr.isEmpty()) { return null; } int dotClassPosition = jarEntryStr.lastIndexOf(".class"); if (0 < dotClassPosition) { jarEntryStr = jarEntryStr.substring(0, jarEntryStr.lastIndexOf(".class")); int lastSeparator = jarEntryStr.lastIndexOf('/'); if (-1 != lastSeparator) { return new String[] { jarEntryStr.substring(0, lastSeparator), jarEntryStr.substring(lastSeparator + 1) }; } else { return new String[] { "", jarEntryStr }; } } return null; } }