/**************************************************************************** * Copyright (C) 2013 HS Coburg. * All rights reserved. * Contact: ecsec GmbH (info@ecsec.de) * * This file is part of the Open eCard App. * * GNU General Public License Usage * This file may be used under the terms of the GNU General Public * License version 3.0 as published by the Free Software Foundation * and appearing in the file LICENSE.GPL included in the packaging of * this file. Please review the following information to ensure the * GNU General Public License version 3.0 requirements will be met: * http://www.gnu.org/copyleft/gpl.html. * * Other Usage * Alternatively, this file may be used in accordance with the terms * and conditions contained in a signed written agreement between * you and ecsec GmbH. * ***************************************************************************/ package org.openecard.addon; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Set; import java.util.jar.JarFile; import java.util.zip.ZipEntry; import org.apache.commons.jci.monitor.FilesystemAlterationListener; import org.apache.commons.jci.monitor.FilesystemAlterationObserver; import org.openecard.addon.manifest.AddonSpecification; import org.openecard.ws.marshal.MarshallingTypeException; import org.openecard.ws.marshal.WSMarshaller; import org.openecard.ws.marshal.WSMarshallerException; import org.openecard.ws.marshal.WSMarshallerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.xml.sax.SAXException; /** * Simple listener for changes in the plugin directory. * <br/>It will add or unload a plugin in the plugin manager if it detects a file creation or removal. * * @author Dirk Petrautzki <petrautzki@hs-coburg.de> */ final class PluginDirectoryAlterationListener implements FilesystemAlterationListener { private static final Logger logger = LoggerFactory.getLogger(PluginDirectoryAlterationListener.class.getName()); private static final String MANIFEST_XML = "META-INF/Addon.xml"; private final FileRegistry fileRegistry; private final WSMarshaller marshaller; PluginDirectoryAlterationListener(FileRegistry fileRegistry) throws WSMarshallerException { this.fileRegistry = fileRegistry; marshaller = WSMarshallerFactory.createInstance(); marshaller.addXmlTypeClass(AddonSpecification.class); } @Override public void onFileDelete(File file) { fileRegistry.unregister(file); } @Override public void onFileCreate(File file) { String name = file.getName(); AddonSpecification abd = getAddonSpecificationFromFile(file); if (abd == null) { return; } Set<AddonSpecification> plugins = fileRegistry.listAddons(); for (AddonSpecification desc : plugins) { if (desc.getId().equals(abd.getId())) { logger.debug("Addon {} is already registered", name); return; } } fileRegistry.register(abd, file); logger.debug("Successfully registered {} as addon", name); } private AddonSpecification getAddonSpecificationFromFile(File file) { String name = file.getName(); JarFile jarFile; AddonSpecification abd; try { jarFile = new JarFile(file); } catch (IOException e) { logger.error("File {} will not be registered as plugin because it's not a JarFile.", name); return null; } try { InputStream manifestStream = getPluginEntryClass(jarFile); if (manifestStream == null) { logger.error("File {} will not be registered as plugin because it doesn't contain a Manifest.xml.", name); return null; } else { marshaller.addXmlTypeClass(AddonSpecification.class); Document manifestDoc = marshaller.str2doc(manifestStream); abd = (AddonSpecification) marshaller.unmarshal(manifestDoc); } } catch (IOException ex) { logger.error("Failed to process Manifest.xml entry for file " + name, ex); return null; } catch (MarshallingTypeException e) { logger.error("Failed to process Manifest.xml entry for file " + name, e); return null; } catch (SAXException e) { logger.error("Failed to process Manifest.xml entry for file " + name, e); return null; } catch (WSMarshallerException e) { logger.error("Failed to process Manifest.xml entry for file " + name, e); return null; } finally { try { jarFile.close(); } catch (IOException ex) { logger.error("Failed to close jar file.", ex); } } return abd; } private InputStream getPluginEntryClass(JarFile jarFile) throws IOException { ZipEntry manifest = jarFile.getEntry(MANIFEST_XML); if (manifest == null) { return null; } else { return jarFile.getInputStream(manifest); } } @Override public void onStop(FilesystemAlterationObserver observer) { // ignore } @Override public void onStart(FilesystemAlterationObserver observer) { // ignore } @Override public void onFileChange(File file) { // ignore } @Override public void onDirectoryDelete(File file) { // ignore } @Override public void onDirectoryCreate(File file) { // ignore } @Override public void onDirectoryChange(File file) { // ignore } }