package org.agnitas.emm.extension.impl;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.agnitas.emm.extension.PluginInstaller;
import org.agnitas.emm.extension.exceptions.DatabaseScriptException;
import org.agnitas.emm.extension.exceptions.MissingPluginManifestException;
import org.agnitas.util.FileUtils;
import org.agnitas.util.FileUtils.ZipEntryNotFoundException;
import org.apache.log4j.Logger;
import org.java.plugin.registry.ManifestInfo;
import org.java.plugin.registry.ManifestProcessingException;
import org.java.plugin.registry.PluginDescriptor;
import org.java.plugin.registry.PluginRegistry;
public class PluginInstallerImpl implements PluginInstaller {
private static final transient Logger logger = Logger.getLogger( PluginInstallerImpl.class);
private final ExtensionSystemConfiguration configuration;
private final JspRestoreUtil jspRestoreUtil;
private final DatabaseScriptExecutor databaseScriptExecutor;
public PluginInstallerImpl( ExtensionSystemConfiguration configuration, JspRestoreUtil jspRestoreUtil, DatabaseScriptExecutor databaseScriptExecutor) {
this.configuration = configuration;
this.jspRestoreUtil = jspRestoreUtil;
this.databaseScriptExecutor = databaseScriptExecutor;
}
@Override
public String installPlugin( String filename) throws IOException, MissingPluginManifestException, DatabaseScriptException {
if( logger.isDebugEnabled()) {
logger.debug( "Plugin ZIP file: " + filename);
}
ZipFile zipFile = new ZipFile( filename);
PluginRegistry registry = org.java.plugin.ObjectFactory.newInstance().createRegistry();
Collection<PluginDescriptor> list = registry.getPluginDescriptors();
for( PluginDescriptor desc : list) {
logger.fatal( "Plugin descriptor: " + desc.getId());
}
if( logger.isInfoEnabled()) {
logger.info( "Installing plugin from file " + filename);
}
ManifestInfo manifestInfo;
try {
manifestInfo = readPluginDataFromManifest( zipFile, registry);
} catch( IOException e) {
logger.warn( "Error reading manifest", e);
throw new MissingPluginManifestException( "Error reading manifest", e);
} catch( ManifestProcessingException e) {
logger.warn( "Error processing manifest", e);
throw new MissingPluginManifestException( "Error processing manifest", e);
}
try {
installPlugin( zipFile, manifestInfo.getId());
} catch( ZipEntryNotFoundException e) {
logger.warn( "Error reading file from ZIP", e);
throw new IOException( "Error reading file from ZIP", e);
} catch( DatabaseScriptException e) {
logger.error( "Error executing database script", e);
throw e;
}
return manifestInfo.getId();
}
private void installPlugin( ZipFile zipFile, String pluginId) throws IOException, ZipEntryNotFoundException, DatabaseScriptException {
if( logger.isInfoEnabled()) {
logger.info( "Installing files for plugin " + pluginId);
}
try {
installFilesFromPluginZip( zipFile, pluginId, configuration);
postInstallationProcessing( pluginId, configuration);
} catch( IOException e) {
logger.error( "Error installing plugin files", e);
removePluginFiles( pluginId);
throw e;
} catch( ZipEntryNotFoundException e) {
logger.error( "Error reading file from ZIP", e);
removePluginFiles( pluginId);
throw e;
} catch( DatabaseScriptException e) {
logger.error( "Error executing database script", e);
removePluginFiles( pluginId);
throw e;
}
}
protected void installFilesFromPluginZip( ZipFile zipFile, String pluginId, ExtensionSystemConfiguration configuration) throws IOException, ZipEntryNotFoundException, DatabaseScriptException {
String pluginDirectory = this.configuration.getPluginDirectory( pluginId);
String jspBackupDirectory = this.configuration.getJspBackupDirectory( pluginId);
String jspDirectory = this.configuration.getJspWorkingDirectory( pluginId);
String databaseScriptsDirectory = this.configuration.getDatabaseScriptsDirectory( pluginId);
if( logger.isDebugEnabled()) {
logger.debug( "plugin directory: " + pluginDirectory);
logger.debug( "JSP backup directory: " + jspBackupDirectory);
logger.debug( "JSP working directory: " + jspDirectory);
logger.debug( "Directory for database scripts: " + databaseScriptsDirectory);
}
installFile( zipFile, "plugin.xml", pluginDirectory, "plugin.xml");
installJsps( zipFile, jspBackupDirectory);
installClasspath( zipFile, pluginDirectory);
installDatabaseScripts( zipFile, databaseScriptsDirectory);
}
protected void postInstallationProcessing( String pluginId, ExtensionSystemConfiguration configuration) throws DatabaseScriptException, IOException {
this.databaseScriptExecutor.execute( new File( this.configuration.getDatabaseInstallScript( pluginId)), pluginId);
this.jspRestoreUtil.createWorkingJspsFromBackupDirectory( pluginId);
}
private ManifestInfo readPluginDataFromManifest( ZipFile zipFile, PluginRegistry registry) throws IOException, ManifestProcessingException, MissingPluginManifestException {
File tempManifestFile = extractManifestToTemporaryFile( zipFile);
try {
URL tempManifestUrl = tempManifestFile.toURI().toURL();
return registry.readManifestInfo( tempManifestUrl);
} finally {
tempManifestFile.delete();
}
}
private File extractManifestToTemporaryFile( ZipFile zipFile) throws IOException, MissingPluginManifestException {
try {
return FileUtils.extractZipEntryToTemporaryFile( zipFile, "plugin.xml", "emm-jpf-");
} catch( ZipEntryNotFoundException e) {
logger.error( "No plugin manifest found", e);
throw new MissingPluginManifestException( "No plugin manifest found", e);
}
}
public void removePluginFiles( String pluginId) {
if( logger.isInfoEnabled()) {
logger.info( "Removing files for plugin " + pluginId);
}
try {
this.databaseScriptExecutor.execute( new File( this.configuration.getDatabaseDeinstallScript( pluginId)), pluginId);
} catch( DatabaseScriptException e) {
logger.error( "Error executing deinstallation script", e);
}
FileUtils.removeRecursively( this.configuration.getJspBackupDirectory( pluginId));
FileUtils.removeRecursively( this.configuration.getJspWorkingDirectory( pluginId));
FileUtils.removeRecursively( this.configuration.getDatabaseScriptsDirectory( pluginId));
FileUtils.removeRecursively( this.configuration.getPluginDirectory( pluginId));
}
private void installJsps( ZipFile zipFile, String baseDirectory) throws IOException, ZipEntryNotFoundException {
if( logger.isInfoEnabled()) {
logger.info( "Installing JPSs");
}
installFilesFromSubdirectory( zipFile, "jsp", baseDirectory, false);
}
private void installClasspath( ZipFile zipFile, String baseDirectory) throws IOException, ZipEntryNotFoundException {
if( logger.isInfoEnabled()) {
logger.info( "Installing class path");
}
installFilesFromSubdirectory( zipFile, "bin", baseDirectory, true);
}
private void installDatabaseScripts( ZipFile zipFile, String baseDirectory) throws IOException, ZipEntryNotFoundException {
if( logger.isInfoEnabled()) {
logger.info( "Installing database scripts");
}
installFilesFromSubdirectory( zipFile, "db", baseDirectory, true);
}
/**
* Install files from a subdirectory in a ZIP file to a directory in the file system.
*
* With <i>excludeUnderscoredDirectories</i> set to true, a special handling is enabled:
* All directories directly located in the specified ZIP subdirectory are checked, if their
* names begin with an underscore ("_"). If so, this directory is skipped.
*
* Here an example: <i>subDirectoryName</i> is set to "example". These files and directories where copied:
* <ul>
* <li>example/fileA.txt</li>
* <li>example/dirA/fileB.txt</li>
* <li>example/dirA/_dirB/fileC.txt (<b>Note:</b> <i>_dirB</i> is not directly located in <i>example</i></li>)
* </ul>
*
* These files and directories where not copied:
* <ul>
* <li>example/_dirC (and all files and directories in <i>_dirC</i>)
* </ul>
*
* @param zipFile the ZIP file containing the files to install
* @param subdirectoryName name of the subdirectory in the ZIP file
* @param baseDirectory name of the base directory in the file system
* @param excludeUnderscoredDirectories when true, all directories beginning with an underscore and that are directly located in the subdirectory of the ZIP are ignored
* @throws IOException on errors reading or writing data
* @throws ZipEntryNotFoundException when a specified ZIP entry was not found
*/
private void installFilesFromSubdirectory( ZipFile zipFile, String subdirectoryName, String baseDirectory, boolean excludeUnderscoredDirectories) throws IOException, ZipEntryNotFoundException {
if( logger.isDebugEnabled()) {
logger.debug( "installing files from ZIP subdirectory (subdirectory in ZIP: " + subdirectoryName + ", target base directory: " + baseDirectory + ", exclude underscored directories: " + (excludeUnderscoredDirectories ? "yes" : "no"));
}
String entryNameStart = FileUtils.removeTrailingSeparator( subdirectoryName) + "/";
Enumeration<? extends ZipEntry> entries = zipFile.entries();
ZipEntry entry = null;
String translatedName;
while( entries.hasMoreElements()) {
entry = entries.nextElement();
if( !entry.getName().startsWith( entryNameStart))
continue;
if( excludeUnderscoredDirectories && entry.getName().startsWith( entryNameStart + "_")) {
if( logger.isInfoEnabled())
logger.info( "skipping underscored directory " + entry.getName());
continue;
}
translatedName = entry.getName().substring( entryNameStart.length());
if( !entry.isDirectory()) {
if( logger.isDebugEnabled()) {
logger.debug( "Installing " + entry.getName() + " to " + baseDirectory);
}
translatedName = entry.getName().substring( entryNameStart.length());
installFile( zipFile, entry.getName(), baseDirectory, translatedName);
} else {
String dirName = baseDirectory + File.separator + translatedName;
if( logger.isDebugEnabled()) {
logger.debug( "Creating directory " + dirName);
}
FileUtils.createPath( dirName);
}
}
}
private void installFile( ZipFile zipFile, String entryName, String baseDirectory, String filename) throws IOException, ZipEntryNotFoundException {
File destinationFile = new File( baseDirectory + File.separator + filename);
if( logger.isDebugEnabled()) {
logger.debug( "Installing ZIP entry " + entryName + " to " + destinationFile.getAbsolutePath());
}
try {
FileUtils.createPathToFile( destinationFile);
FileUtils.extractZipEntryToFile( zipFile, entryName, destinationFile);
} catch( ZipEntryNotFoundException e) {
logger.error( "Specified ZIP entry " + entryName + " not found", e);
destinationFile.delete();
throw e;
} catch( IOException e) {
logger.error( "Error installing file " + filename, e);
destinationFile.delete();
throw e;
}
}
@Override
public void uninstallPlugin( String pluginId) {
uninstallPlugin( pluginId, this.configuration);
}
protected void uninstallPlugin( String pluginId, ExtensionSystemConfiguration configuration) {
removePluginFiles( pluginId);
}
}