/** * Copyright (c) 2009 Juwi MacMillan Group GmbH * * 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 de.juwimm.cms.content.modules; import static de.juwimm.cms.client.beans.Application.getBean; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.security.AllPermission; import java.security.CodeSource; import java.security.PermissionCollection; import java.util.ArrayList; import java.util.Collection; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Properties; import javax.swing.JPanel; import javax.swing.SwingUtilities; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.log4j.Logger; import org.tizzit.util.XercesHelper; import org.w3c.dom.Element; import org.w3c.dom.Node; import de.juwimm.cms.Messages; import de.juwimm.cms.client.beans.Beans; import de.juwimm.cms.common.Constants; import de.juwimm.cms.gui.controls.ColapsePanel; import de.juwimm.cms.util.Communication; import de.juwimm.cms.vo.SiteValue; /** * <p>Title: </p> * <p>Description: </p> * <p>Copyright: Copyright (c) 2002</p> * <p>Company: </p> * @author <a href="mailto:s.kulawik@juwimm.com">Sascha-Matthias Kulawik</a> * @version $Id$ */ public class ModuleFactoryStandardImpl implements ModuleFactory { private static Logger log = Logger.getLogger(ModuleFactoryStandardImpl.class); /** Contains all the Modules used in one Template key DcfName value Module */ private Hashtable<String, Module> htModules = new Hashtable<String, Module>(); /** Contains the initial content from the DCF for every dcfName */ private final Hashtable<String, Node> htInitialContent = new Hashtable<String, Node>(); public ModuleFactoryStandardImpl() { htModules = new Hashtable<String, Module>(); } public Module getModuleInstanceUnconfigured(String classname, List<String> additionalJarFiles) { Module module = null; if (!additionalJarFiles.isEmpty()) { module = loadPlugins(classname, additionalJarFiles); } else { try { Class handlerClass = Class.forName(classname); module = (Module) handlerClass.newInstance(); } catch (ClassNotFoundException exe) { log.error("ClassNotFoundException", exe); } catch (IllegalAccessException exe) { log.error("IllegalAccessException", exe); } catch (InstantiationException exe) { log.error("InstantiationException", exe); } } return module; } public Module loadPlugins(String classname, List<String> additionalJarFiles) { Module module = null; Communication comm = ((Communication) getBean(Beans.COMMUNICATION)); SiteValue site = comm.getCurrentSite(); String urlPath = site.getDcfUrl(); final String userHome = System.getProperty("user.home"); final String fileSeparator = System.getProperty("file.separator"); StringBuffer pluginCachePath = new StringBuffer(userHome); pluginCachePath.append(fileSeparator); pluginCachePath.append(".tizzitCache"); pluginCachePath.append(fileSeparator); pluginCachePath.append("plugins"); pluginCachePath.append(fileSeparator); pluginCachePath.append(Constants.SERVER_HOST); pluginCachePath.append(fileSeparator); final String pluginPath = pluginCachePath.toString(); final int addSize = additionalJarFiles.size(); /* enthaelt alle Jar files die sich nicht auf dem lokalen Rechner befinden */ ArrayList<String> httpLoad = new ArrayList<String>(); for (int i = 0; i < addSize; i++) { String filePath = pluginPath + additionalJarFiles.get(i); File tempFile = new File(filePath); if (!tempFile.exists()) { httpLoad.add(additionalJarFiles.get(i)); } } if (httpLoad.size() > 0) { File dir = new File(pluginPath); if (!dir.exists()) { if (log.isDebugEnabled()) log.debug("Going to create plugin directory..."); boolean ret = dir.mkdirs(); if (!ret) { log.warn("Could not create plugin directory"); } } /* laedt die Jarfiles auf den lokalen Rechner herunter */ HttpClient httpclient = new HttpClient(); for (int i = 0; i < httpLoad.size(); i++) { String url = urlPath + httpLoad.get(i); if (log.isDebugEnabled()) log.debug("Plugin URL " + url); HttpMethod method = new GetMethod(url); try { int status = httpclient.executeMethod(method); if (status == HttpStatus.SC_OK) { File file = new File(pluginPath + httpLoad.get(i)); byte[] data = method.getResponseBody(); if (log.isDebugEnabled()) log.debug("Received " + data.length + " bytes of data"); FileOutputStream output = new FileOutputStream(file); output.write(data); output.close(); } else { log.warn("No OK received"); } } catch (HttpException htex) { log.warn("HTTP exception " + htex.getMessage()); } catch (IOException ioe) { log.warn("IO exception " + ioe.getMessage()); } method.releaseConnection(); } } try { if (log.isDebugEnabled()) log.debug("Creating URL"); URL[] url = new URL[addSize]; for (int i = 0; i < addSize; i++) { String jarModule = additionalJarFiles.get(i); String jarPath = "file:///" + pluginPath + jarModule; if (log.isDebugEnabled()) log.debug("Jar path " + jarPath); url[i] = new URL(jarPath); } URLClassLoader cl = this.getURLClassLoader(url); //URLClassLoader cl = new URLClassLoader(url, this.getClass().getClassLoader()); if (log.isDebugEnabled()) log.debug("Created URL classloader"); Class c = cl.loadClass(classname); if (log.isDebugEnabled()) log.debug("Created class"); module = (Module) c.newInstance(); if (log.isDebugEnabled()) log.debug("Got the module"); } catch (Exception loadex) { log.warn(loadex.getClass().toString()); log.error("Cannot load from URL " + loadex.getMessage(), loadex); } return module; } public Iterator<Module> getAllModules() { return htModules.values().iterator(); } /** * Creates a {@link Module} and configures it with all information taken from the mandator's dcf. * * @param dcfelement the randomly named element containing the {@code dcfconfig} element * @param contentdata XML content for the module (the way TIZZIT saves it) * * @see de.juwimm.cms.content.modules.ModuleFactory#getModuleInstance(org.w3c.dom.Element, org.w3c.dom.Node) */ public Module getModuleInstance(Element dcfElement, Node contentdata) { Module module = null; String classname = ""; List<String> jarClassPath = new ArrayList<String>(); try { classname = XercesHelper.getNodeValue(dcfElement, "./dcfConfig/classname"); Iterator it = XercesHelper.findNodes(dcfElement, "./dcfConfig/classpath/jar"); while (it.hasNext()) { Node node = (Node) it.next(); jarClassPath.add(XercesHelper.getNodeValue(node)); } module = getModuleInstanceUnconfigured(classname, jarClassPath); String label = dcfElement.getAttribute("label"); String description = dcfElement.getAttribute("description"); String dcfname = dcfElement.getAttribute("dcfname"); String mandatory = XercesHelper.getNodeValue(dcfElement, "./dcfConfig/mandatory"); module.setLabel(label); module.setDescription(description); module.setName(dcfname); if (mandatory.equals("true")) { module.setMandatory(true); } else { module.setMandatory(false); } // Fill the custom properties for this dcfmodule explicit for this DCF Iterator ni = XercesHelper.findNodes(dcfElement, "./dcfConfig/property"); setCustomProperties(module, ni); if (module instanceof Iteration) { Node itEl = XercesHelper.findNode(dcfElement, "./dcfConfig/iterationElements"); ((Iteration) module).setIterationElements(itEl); } //Load initial config if there is no current content Node ndeInitialContent = XercesHelper.findNode(dcfElement, "./dcfInitial"); if (ndeInitialContent != null) { htInitialContent.put(module.getName(), ndeInitialContent); } if (contentdata == null) { contentdata = ndeInitialContent; } if (contentdata != null) { module.setProperties(contentdata); } } catch (Exception exe) { log.error("Error getting Module instance", exe); } this.htModules.put(module.getName(), module); return module; } /** * Parses the specified {@code propertyNodes} and prepares the information, * calls {@see Module#setCustomProperties(String, Properties)} * * @see ModuleFactory#setCustomProperties(Module, java.util.Iterator) */ public void setCustomProperties(Module module, Iterator propertyNodes) { try { Element property; while (propertyNodes.hasNext()) { property = (Element) propertyNodes.next(); String propertyname = property.getAttribute("name"); Properties prop = new Properties(); Iterator pi = XercesHelper.findNodes(property, "./*"); Node propParameter; while (pi.hasNext()) { propParameter = (Element) pi.next(); String propVal = ""; if (propParameter.getNodeName().equalsIgnoreCase("properties")) { propVal = XercesHelper.node2string(propParameter); } else { propVal = XercesHelper.getNodeValue(propParameter); } prop.setProperty(propParameter.getNodeName(), propVal); } module.setCustomProperties(propertyname, prop); } module.setCustomProperties("CustomConfigurationReady", new Properties()); } catch (Exception exe) { log.error("Error setting custom properties", exe); } if (log.isDebugEnabled()) log.debug("SetModuleParameters end " + module.getName()); } public Module getModuleByDCFName(String dcfname) { return this.htModules.get(dcfname); } public void reconfigureModules(Hashtable<String, Element> htModuleDcfNameDcfElement) { Collection coll = this.htModules.values(); Iterator it = coll.iterator(); while (it.hasNext()) { Module module = (Module) it.next(); Thread t = new Thread(Thread.currentThread().getThreadGroup(), new ReconfigureModuleRunnable(module, htModuleDcfNameDcfElement, htInitialContent)); t.setPriority(Thread.NORM_PRIORITY); t.setName("ReconfigureModuleRunnable"); t.start(); } } /** * Thread calling <code>setProperties(Node)</code> on the given Module with the Node from the DCF */ public class ReconfigureModuleRunnable implements Runnable { private Hashtable<String, Element> htModuleDcfNameDcfElement = null; private Hashtable<String, Node> htInitialContent = null; private Module module = null; public ReconfigureModuleRunnable(Module module, Hashtable<String, Element> htModuleDcfNameDcfElement, Hashtable<String, Node> htInitialContent) { this.module = module; this.htModuleDcfNameDcfElement = htModuleDcfNameDcfElement; this.htInitialContent = htInitialContent; } public void run() { Node content = htModuleDcfNameDcfElement.get(module.getName()); if (content != null && content.hasChildNodes()) { module.setProperties(content); } else { module.setProperties(htInitialContent.get(module.getName())); } } } public JPanel getPanelForModule(Module module) { //ContentBorder cb = new ContentBorderModulePanel(); //cb.setContentModulePanel(module.viewPanelUI()); //cb.setLabel(module.getLabel()); ColapsePanel cbb = new ColapsePanel(); cbb.setText(module.getLabel()); cbb.add(module.viewPanelUI()); return cbb; } public String isModuleValid() { String retVal = ""; Iterator it = this.htModules.values().iterator(); while (it.hasNext()) { Module mod = (Module) it.next(); log.info("VALIDATION: FOUND MOD " + mod.getName()); if (!mod.isModuleValid()) { if (!retVal.equals("")) retVal += "<br><hr>"; String prepend = Messages.getString("content.moduleFactory.validationPrepend", mod.getLabel()); String errMsg = mod.getValidationError(); errMsg = errMsg.replaceAll("[\n]", "<br>"); retVal += "<font face=\"Arial, Helvetica, sans-serif\"><b>" + prepend + "</b><br>" + errMsg + "</font>"; } } return retVal; } public void setEnabled(boolean enable) { Iterator it = this.htModules.values().iterator(); while (it.hasNext()) { Module mod = (Module) it.next(); SwingUtilities.invokeLater(new SetEnabled(mod, enable)); } } /** * */ private class SetEnabled implements Runnable { private final Module mod; private final boolean enable; public SetEnabled(Module mod, boolean enable) { this.mod = mod; this.enable = enable; } public void run() { try { mod.setEnabled(enable); } catch (Exception exe) { log.error("Error setting enabled in setEnabled thread", exe); } } } private URLClassLoader getURLClassLoader(URL[] url) { ClassLoader previousClassLoader = Thread.currentThread().getContextClassLoader(); URLClassLoader cl = new PluginURLClassLoader(url, previousClassLoader); Thread.currentThread().setContextClassLoader(cl); return cl; } /** * Special URLClassLoader granting all permissions * @author <a href="mailto:carsten.schalm@juwimm.com">Carsten Schalm</a> * company Juwi|MacMillan Group Gmbh, Walsrode, Germany * @version $Id$ */ public class PluginURLClassLoader extends URLClassLoader { public PluginURLClassLoader(URL[] url, ClassLoader parent) { super(url, parent); } @Override protected PermissionCollection getPermissions(CodeSource codesource) { PermissionCollection perms = super.getPermissions(codesource); perms.add(new AllPermission()); return perms; } } }