Java tutorial
/* Copyright (c) 2007 Pentaho Corporation. All rights reserved. * This software was developed by Pentaho Corporation and is provided under the terms * of the GNU Lesser General Public License, Version 2.1. You may not use * this file except in compliance with the license. If you need a copy of the license, * please go to http://www.gnu.org/licenses/lgpl-2.1.txt. The Original Code is Pentaho * Data Integration. The Initial Developer is Pentaho Corporation. * * Software distributed under the GNU Lesser Public License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to * the license for the specific language governing your rights and limitations.*/ package com.panet.imeta.job; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.apache.commons.vfs.FileSystemException; import org.springframework.core.io.FileSystemResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import com.panet.imeta.core.Const; import com.panet.imeta.core.PDIClassLoader; import com.panet.imeta.core.config.ConfigManager; import com.panet.imeta.core.config.KettleConfig; import com.panet.imeta.core.exception.KettleConfigException; import com.panet.imeta.core.exception.KettleException; import com.panet.imeta.core.exception.KettleStepLoaderException; import com.panet.imeta.core.plugins.PluginLoader; import com.panet.imeta.i18n.LanguageChoice; import com.panet.imeta.i18n.LoaderInputStreamProvider; import com.panet.imeta.job.entry.JobEntryBase; import com.panet.imeta.job.entry.JobEntryInterface; /** * Takes care of loading job-entries or job-entry plugins. * * @since 9-may-2005 * @author Matt * */ public class JobEntryLoader implements LoaderInputStreamProvider { private static JobEntryLoader jobEntryLoader = null; private List<JobPlugin> pluginList; private Hashtable<String, URLClassLoader> classLoaders; private boolean initialized; private JobEntryLoader() { pluginList = new ArrayList<JobPlugin>(); classLoaders = new Hashtable<String, URLClassLoader>(); initialized = false; } public static final JobEntryLoader getInstance() { if (jobEntryLoader != null) return jobEntryLoader; jobEntryLoader = new JobEntryLoader(); return jobEntryLoader; } /** * Read all native and plug-in job entries * @return true if all went well * @throws KettleException in case an error occurs. * @deprecated in favor of static method init() to flag the exception throwing in this method. (change of contract) */ public boolean read() throws KettleException { readNatives(); readPlugins(); initialized = true; return true; } /** * Read all native and plug-in job entries * @throws KettleException In case a plug-in could not be loaded or something else went wrong in the process. */ public static final void init() throws KettleException { JobEntryLoader loader = getInstance(); loader.readNatives(); loader.readPlugins(); loader.initialized = true; } public boolean readNatives() { try { // annotated classes first ConfigManager<?> jobsAnntCfg = KettleConfig.getInstance().getManager("jobs-annotation-config"); Collection<JobPluginMeta> jobs = jobsAnntCfg.loadAs(JobPluginMeta.class); ConfigManager<?> jobsCfg = KettleConfig.getInstance().getManager("jobs-xml-config"); Collection<JobPluginMeta> cjobs = jobsCfg.loadAs(JobPluginMeta.class); jobs.addAll(cjobs); for (JobPluginMeta job : jobs) if (job.getType() != JobEntryType.NONE) pluginList.add(new JobPlugin(JobPlugin.TYPE_NATIVE, job.getId(), job.getType(), job.getTooltipDesc(), null, null, job.getImageFileName(), job.getClassName().getName(), job.getCategory())); } catch (KettleConfigException e) { e.printStackTrace(); return false; } return true; } /** * The 'new' method. Uses plugin loader, which uses VFS to load plugins. */ public boolean readPlugins() { try { PluginLoader loader = PluginLoader.getInstance(); loader.load("plugins-config"); pluginList.addAll(loader.getDefinedPlugins(JobPlugin.class)); return true; } catch (KettleConfigException e) { e.printStackTrace(); return false; } } public JobEntryInterface getJobEntryClass(String desc) throws KettleStepLoaderException { JobPlugin jp = findJobEntriesWithDescription(desc); return getJobEntryClass(jp); } public Class<?> loadClass(JobPlugin sp, String className) throws KettleStepLoaderException { try { switch (sp.getType()) { case JobPlugin.TYPE_NATIVE: return Class.forName(className); case JobPlugin.TYPE_PLUGIN: ClassLoader cl = getClassLoader(sp); return cl.loadClass(className); default: throw new KettleStepLoaderException("Unknown plugin type : " + sp.getType()); } } catch (Exception e) { throw new KettleStepLoaderException(e); } } public Class<?> loadClass(String desc, String className) throws KettleStepLoaderException { try { return loadClass(findJobEntriesWithDescription(desc), className); } catch (Exception e) { throw new KettleStepLoaderException(e); } } public Class<?> loadClassByID(String id, String className) throws KettleStepLoaderException { try { return loadClass(findJobEntriesWithID(id), className); } catch (Exception e) { throw new KettleStepLoaderException(e); } } public JobEntryInterface getJobEntryClass(JobPlugin sp) throws KettleStepLoaderException { if (sp != null) { try { Class<?> cl = null; switch (sp.getType()) { case JobPlugin.TYPE_NATIVE: { cl = Class.forName(sp.getClassname()); } break; case JobPlugin.TYPE_PLUGIN: { ClassLoader ucl = getClassLoader(sp); // What's the protection domain of this class? // ProtectionDomain protectionDomain = // this.getClass().getProtectionDomain(); // Load the class. // Thread.currentThread().setContextClassLoader(ucl); cl = ucl.loadClass(sp.getClassname()); } break; default: throw new KettleStepLoaderException("Unknown plugin type : " + sp.getType()); } JobEntryInterface res = (JobEntryInterface) cl.newInstance(); if (sp.getType() == JobPlugin.TYPE_PLUGIN) { res.setPluginID(sp.getID()); } res.setDescription(sp.getDescription()); res.setName(sp.getID()); res.setConfigId(sp.getID()); // set the type if (res.getJobEntryType() == null) res.setJobEntryType(sp.getJobType()); return res; } catch (ClassNotFoundException e) { throw new KettleStepLoaderException("Class not found", e); } catch (InstantiationException e) { throw new KettleStepLoaderException("Unable to instantiate class", e); } catch (IllegalAccessException e) { throw new KettleStepLoaderException("Illegal access to class", e); } catch (Throwable e) { throw new KettleStepLoaderException("Unexpected error loading class", e); } } else { throw new KettleStepLoaderException("No valid step/plugin specified (plugin=null)."); } } private ClassLoader getClassLoader(JobPlugin sp) throws FileSystemException, MalformedURLException { String jarfiles[] = sp.getJarfiles(); List<URL> classpath = new ArrayList<URL>(); // safe to use filesystem because at this point it is all local // and we are using this so we can do things like */lib/*.jar and so // forth, as with ant ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(new FileSystemResourceLoader()); for (int i = 0; i < jarfiles.length; i++) { try { Resource[] paths = resolver.getResources(jarfiles[i]); for (Resource path : paths) { classpath.add(path.getURL()); } } catch (IOException e) { e.printStackTrace(); continue; } } URL urls[] = classpath.toArray(new URL[] {}); // Load the class!! // // First get the class loader: get the one that's the // webstart classloader, not the thread classloader // ClassLoader classLoader = getClass().getClassLoader(); // ClassLoader classLoader = getClass().getClassLoader(); // Construct a new URLClassLoader based on this one... URLClassLoader ucl = (URLClassLoader) classLoaders.get(sp.getID()); if (ucl == null) { synchronized (classLoaders) { ucl = new PDIClassLoader(urls, classLoader); classLoaders.put(sp.getID(), ucl); // save for later use... } } return ucl; } /** * Count's the number of steps with a certain type. * * @param type * One of StepPlugin.TYPE_NATIVE, StepPlugin.TYPE_PLUGIN, * StepPlugin.TYPE_ALL * @return The number of steps with a certain type. */ public int nrJobEntriesWithType(int type) { int nr = 0; for (int i = 0; i < pluginList.size(); i++) { JobPlugin sp = (JobPlugin) pluginList.get(i); if (sp.getType() == type || type == JobPlugin.TYPE_ALL) nr++; } return nr; } public JobPlugin getJobEntryWithType(int type, int index) { int nr = 0; for (int i = 0; i < pluginList.size(); i++) { JobPlugin sp = (JobPlugin) pluginList.get(i); if (sp.getType() == type || type == JobPlugin.TYPE_ALL) { if (nr == index) return sp; nr++; } } return null; } /** * @param stepid * @return The StepPlugin for the step with the specified ID. Null is * returned when the ID couldn't be found! */ public JobPlugin findJobPluginWithID(String stepid) { for (int i = 0; i < pluginList.size(); i++) { JobPlugin sp = (JobPlugin) pluginList.get(i); if (sp.getID().equalsIgnoreCase(stepid)) return sp; } return null; } public JobPlugin[] getJobEntriesWithType(int type) { int nr = nrJobEntriesWithType(type); JobPlugin steps[] = new JobPlugin[nr]; for (int i = 0; i < steps.length; i++) { JobPlugin sp = getJobEntryWithType(type, i); // System.out.println("sp #"+i+" = "+sp.getID()); steps[i] = sp; } return steps; } public JobPlugin findJobEntriesWithID(String stepid) { for (int i = 0; i < pluginList.size(); i++) { JobPlugin sp = (JobPlugin) pluginList.get(i); if (sp.getID().equalsIgnoreCase(stepid)) return sp; } return null; } public JobPlugin findJobEntriesWithClassName(String cn) { for (int i = 0; i < pluginList.size(); i++) { JobPlugin sp = (JobPlugin) pluginList.get(i); if (sp.getClassname().equalsIgnoreCase(cn)) return sp; } return null; } public JobPlugin findJobEntriesWithDescription(String description) { for (int i = 0; i < pluginList.size(); i++) { JobPlugin sp = (JobPlugin) pluginList.get(i); if (sp.getDescription().equalsIgnoreCase(description) || sp.getID().equalsIgnoreCase(description)) return sp; } return null; } /** * Determine the step's id based upon the StepMetaInterface we get... * * @param jei * The StepMetaInterface * @return the step's id or null if we couldn't find anything. */ public String getJobEntryID(JobEntryInterface jei) { for (int i = 0; i < nrJobEntriesWithType(JobPlugin.TYPE_ALL); i++) { JobPlugin sp = getJobEntryWithType(JobPlugin.TYPE_ALL, i); if (jei.getClass().getName().equals(sp.getClassname())) return sp.getID(); } return null; } /** * @return Returns the initialized. */ public boolean isInitialized() { return initialized; } /** * @param initialized * The initialized to set. */ public void setInitialized(boolean initialized) { this.initialized = initialized; } /** * Search through all jarfiles in all steps and try to find a certain file * in it. * * @param filename * @return an inputstream for the given file. */ public InputStream getInputStreamForFile(String filename) { JobPlugin[] jobplugins = getJobEntriesWithType(JobPlugin.TYPE_PLUGIN); for (JobPlugin jobPlugin : jobplugins) { try { String[] jarfiles = jobPlugin.getJarfiles(); if (jarfiles != null) { for (int j = 0; j < jarfiles.length; j++) { JarFile jarFile = new JarFile(jarfiles[j]); JarEntry jarEntry; if (filename.startsWith("/")) { jarEntry = jarFile.getJarEntry(filename.substring(1)); } else { jarEntry = jarFile.getJarEntry(filename); } if (jarEntry != null) { InputStream inputStream = jarFile.getInputStream(jarEntry); if (inputStream != null) { return inputStream; } } } } } catch (Exception e) { // Just look for the next one... } } return null; } /** * @return a unique array of all the job entry plugin package names */ public String[] getPluginPackages() { List<String> list = new ArrayList<String>(); for (JobPlugin stepPlugin : pluginList) { String className = stepPlugin.getClassname(); int lastIndex = className.lastIndexOf("."); String packageName = className.substring(0, lastIndex); if (!list.contains(packageName)) list.add(packageName); } Collections.sort(list); return list.toArray(new String[list.size()]); } /** * Get a unique list of categories. We can use this to display in trees etc. * * @param type The type of job entry plugins for which we want to categories... * @return a unique list of categories */ public String[] getCategories(int type) { return getCategories(type, LanguageChoice.getInstance().getDefaultLocale().toString().toLowerCase()); } /** * Get a unique list of categories. We can use this to display in trees etc. * * @param type The type of job entry plugins for which we want to categories... * @return a unique list of categories */ public String[] getCategories(int type, String locale) { Hashtable<String, String> cat = new Hashtable<String, String>(); for (int i = 0; i < nrJobEntriesWithType(type); i++) { JobPlugin sp = getJobEntryWithType(type, i); if (sp != null) { cat.put(sp.getCategory(locale), sp.getCategory(locale)); } } Enumeration<String> keys = cat.keys(); String retval[] = new String[cat.size()]; int i = 0; while (keys.hasMoreElements()) { retval[i] = keys.nextElement(); i++; } // Sort the resulting array... // It has to be sorted the same way as the String array JobStep.category_order // for (int a = 0; a < retval.length; a++) { for (int b = 0; b < retval.length - 1; b++) { // What is the index of retval[b] and retval[b+1]? // int idx1 = Const.indexOfString(retval[b], JobEntryBase.category_order); int idx2 = Const.indexOfString(retval[b + 1], JobEntryBase.category_order); if (idx1 > idx2) { String dummy = retval[b]; retval[b] = retval[b + 1]; retval[b + 1] = dummy; } } } return retval; } public List<Object[]> getPluginInformation() { List<Object[]> list = new ArrayList<Object[]>(); for (JobPlugin plugin : pluginList) { list.add(plugin.getPluginInformation()); } return list; } }