Package kameleon.util

Source Code of kameleon.util.IOPlugIn

/*
* Copyright (c) 2012, Fromentin Xavier, Schnell Michaël, Dervin Cyrielle, Brabant Quentin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*      * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*      * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*      * The names of its contributors may not be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Fromentin Xavier, Schnell Michaël, Dervin Cyrielle OR Brabant Quentin
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package kameleon.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import kameleon.exception.FileDeletingException;
import kameleon.exception.FileReadingException;
import kameleon.exception.FileWritingException;
import kameleon.exception.InvalidPlugInException;
import kameleon.exception.KameleonException;
import kameleon.exception.UnknownPlugInException;
import kameleon.plugin.AnalyzerManager;
import kameleon.plugin.PlugIn;
import kameleon.plugin.PlugInInfo;

/**
* Utility class for writing, reading and loading plug-ins and their files.
*
* @author    Schnell Michaël
* @version    1.0 
*/
public class IOPlugIn implements FileConstants {

  /**
   * Utility constant for the absolute path of a jar file.
   */
  private static final String JAR_FILE =
      String.format("%%s%s%%s.jar", File.separator) ; //$NON-NLS-1$

  /**
   * Manager which should be updated when a plug-in is added or removed.
   */
  protected AnalyzerManager am ;

  /**
   * Absolute path of the folder containing the java archives of
   * the analyzer plug-ins.
   */
  protected String analyzerFolder ;

  /**
   * Absolute path of the folder containing the java archives of
   * the generator plug-ins.
   */
  protected String generatorFolder ;

  /**
   * Absolute path of the file containing the information about
   * the analyzer plug-ins.
   */
  protected String analyzerConfigFile ;

  /**
   * Absolute path of the file containing the information about
   * the generator plug-ins.
   */
  protected String generatorConfigFile ;

  /**
   * Absolute path of the folder containing the resources for the plug-ins.
   */
  protected String ressourceFolder ;

  /**
   * Sole constructor.
   *
   * @param   am
   *       manager which should be updated when a plug-in is added or removed
   *
   * @param   analyzerFolder
   *       absolute path of the folder containing the java archives of
   *       the analyzer plug-ins
   *
   * @param   generatorFolder
   *       absolute path of the folder containing the java archives of
   *       the generator plug-ins
   *
   * @param   analyzerConfigFile
   *       absolute path of the file containing the information about
   *       the analyzer plug-ins
   *
   * @param   generatorConfigFile
   *       absolute path of the file containing the information about
   *       the generator plug-ins
   *
   * @param   ressourceFolder
   *       absolute path of the folder containing the resources for the plug-ins
   */
  public IOPlugIn(AnalyzerManager am, String analyzerFolder,
      String generatorFolder, String analyzerConfigFile,
      String generatorConfigFile, String ressourceFolder) {
    super();
    this.am = am;
    this.analyzerFolder = analyzerFolder;
    this.generatorFolder = generatorFolder;
    this.analyzerConfigFile = analyzerConfigFile;
    this.generatorConfigFile = generatorConfigFile;
    this.ressourceFolder = ressourceFolder;
  }// IOPlugIn(AnalyzerManager, String, String, String, String, String)

  /**
   * Adds a given plug-in to the software.
   *
   * <p>For a plug-in to be valid, the source file should be a valid java
   * archive. Inside this archive, there should be two things:
   * <ol>
   * <li>A file {@code plugin.info} containing a serialized instance of {@code PlugInInfo}.
   * This file contains the informations about the plug-in.
   * <li>The executable code for the plug-in (usual content of a java archive, that is {@code .class} files).
   * </ol>
   *
   * <p>The function copies the jar into the correct folder (analyzer of
   * generator folder depending on the type of plug-in) and adds the information about
   * the new plug-in in the concerned configuration file. If an analyzer plug-in was added,
   * the analyzer manager is notified. Finally the pictures for the given plug-in are
   * copied into the resource folder.
   *
   * <p><b>Should any of these steps fail, the software is left in an unstable state.
   * No actions are undone !</b>
   *
   * @param   plugin
   *       plug-in source file
   *
   * @return  instance of {@code PlugInInfo} with the information about the newly added plug-in
   *
   * @throws   FileReadingException
   *       if an error occurred while reading
   * 
   * @throws   FileWritingException
   *       if an error occurred while writing
   *
   * @throws  InvalidPlugInException
   *       if the given plug-in is invalid
   */
  //TODO Undo intermediate state
  public PlugInInfo addPlugIn(File plugin)
      throws InvalidPlugInException, FileReadingException, FileWritingException {
    try {
      ZipFile plugInZip = new ZipFile(plugin) ;
      /* Retrieve plug-in information object */
      ZipEntry infoFileEntry = plugInZip.getEntry(PLUGIN_INFO_FILE_NAME) ;
      if (infoFileEntry == null) {
        throw new InvalidPlugInException(plugin.getName()) ;
      }// if
      PlugInInfo info = (PlugInInfo) IOObject.readObjectFromFile(
          new BufferedInputStream(
              plugInZip.getInputStream(infoFileEntry))) ;

      // Close the zip file in order to copy it
      plugInZip.close() ;

      /* Determine the nature of the plug-in before adding it */
      if (info.isAnalyzer()) {
        addJar(plugin, info, this.analyzerFolder) ;
        addConfiguration(info, this.analyzerConfigFile) ;
        this.am.addAnalyzerInfo(info) ;
      } else {
        addJar(plugin, info, this.generatorFolder) ;
        addConfiguration(info, this.generatorConfigFile) ;
      }// if
      copyImages(info) ;
     
      return info ;
    } catch (ZipException e) {
      throw new InvalidPlugInException(plugin.getName()) ;
    } catch (IOException e) {
      throw new FileReadingException(plugin) ;
    }// try
  }// addPlugIn(File, String, String, String)

  /**
   * Copies the given zip file into the given folder.
   *
   * @param   pluginFile
   *       source zip file
   *
   * @param   info
   *       instance of {@code PlugInInfo} for the given plug-in
   *
   * @param   tagetFolder
   *       target folder for the extracted jar
   * 
   * @throws   FileReadingException
   *       if an error occurred while reading
   * 
   * @throws   FileWritingException
   *       if an error occurred while writing
   */
  private static void addJar(File pluginFile, PlugInInfo info, String tagetFolder)
      throws FileReadingException, FileWritingException {
    String jarName = String.format("%s.jar", info.getJarName()) ; //$NON-NLS-1$
   
    /* Create the streams */
    InputStream src ;
    try {
      src = new BufferedInputStream(
          new FileInputStream(pluginFile)) ;
    } catch (IOException e) {
      throw new FileReadingException() ;
    }// try
    OutputStream dest ;
    try {
      dest = new BufferedOutputStream(
          new FileOutputStream(String.format(PATH_EXTENSION,
              tagetFolder, jarName))) ;
    } catch (IOException e) {
      throw new FileWritingException() ;
    }// try

    /* Do the actual copying */
    IOFile.copyFile(src, dest) ;

    /* Close the streams */
    try {
      src.close() ;
    } catch (IOException e) {
      throw new FileReadingException() ;
    }// try
    try {
      dest.close() ;
    } catch (IOException e) {
      throw new FileWritingException() ;
    }// try
  }// addJar(ZipFile, PlugInInfo, String)

  /**
   * Adds the given {@code PlugInInfo} to the list read in the given
   * configuration file.
   *
   * @param   info
   *       instance to add in the list
   *
   * @param   configFile
   *       source configuration file for the list
   * 
   * @throws   FileWritingException
   *       if an error occurred while writing
   */
  private static void addConfiguration(PlugInInfo info, String configFile) throws FileWritingException
  {
    List<PlugInInfo> knownPlugIns = null ;
    try {
      knownPlugIns = IOObject.readList(configFile) ;
    } catch (KameleonException e) {
      knownPlugIns = new LinkedList<PlugInInfo>() ;
    }// try

    int index = knownPlugIns.indexOf(info) ;
    // Plug-in is new
    if (index == -1) {
      knownPlugIns.add(info) ;
    } else {// Plug-in is being updated
      knownPlugIns.set(index, info) ;
    }// if

    IOObject.writeObjectToFile(configFile, knownPlugIns) ;
  }// addConfiguration(PlugInInfo, String)

  /**
   * Removes the given plug-in from the software.
   *
   * @param  info
   *       information about the plug-in which should be removed
   *
//   * @throws   FileDeletingException
//   *       if files used by the plug-in could not be deleted
   *
   * @throws  UnknownPlugInException
   *       if the removed plug-in does not exist
   */
  //TODO Undo intermediate state
  public void removePlugIn(PlugInInfo info)
      throws /*FileDeletingException, */UnknownPlugInException {
    String configFile, folder ;
    if (info.isAnalyzer()) {
      configFile = this.analyzerConfigFile ;
      folder = this.analyzerFolder ;
    } else {
      configFile = this.generatorConfigFile ;
      folder = this.generatorFolder ;
    }// if
    removeConfiguration(info, configFile) ;
    try {
      removeJar(folder, info.getJarName()) ;
      //TODO Handle the case of partially removed pictures
      removeImages(info) ;
    } catch (KameleonException ke) {
      // TODO: handle exception
      forceRemoveImages(info) ;
    }// try
  }// removePlugIn(PlugInInfo)

  /**
   * Deleted the given jar file from the given folder.
   *
   * @param   parentFolder
   *       folder containing the jar file which should be deleted
   *
   * @param   jarName
   *       name of the jar file which should be deleted (without the extension)
   *
   * @throws   FileDeletingException
   *       if the jar of the plug-in could not be deleted
   */
  private static void removeJar(String parentFolder, String jarName)
      throws FileDeletingException {
    String jarPath = String.format(JAR_FILE, parentFolder, jarName) ;
    File jar = new File(jarPath) ;
    boolean sucess = jar.delete() ;
    if (!sucess) {
      throw new FileDeletingException(jar) ;
    }// if
  }// removeJar(String)

  /**
   * Removes the given instance of {@code PlugInInfo} from the list read
   * in the given configuration file. The configuration file is updated
   * after the removal.
   *
   * @param   info
   *       instance of [@ode PlugInInfo] which should be removed
   *
   * @param   configFile
   *       source configuration file for the list from which the
   *       instance should be removed
   *
   * @throws  UnknownPlugInException
   *       if the given plug-in is not listed in the configuration file
   *///TODO Review thrown exceptions
  private static void removeConfiguration(PlugInInfo info, String configFile)
      throws UnknownPlugInException {
    try {
      List<PlugInInfo> knownPlugIns = IOObject.readList(configFile) ;

      // Plug-in is unknown
      if (!knownPlugIns.contains(info)) {
        throw new UnknownPlugInException(info) ;
      }// if

      // Remove plug-in info and save the result
      knownPlugIns.remove(info) ;
      IOObject.writeObjectToFile(configFile, knownPlugIns) ;
    } catch(UnknownPlugInException uke) {
      throw uke ;
    } catch(KameleonException ke) {
      /* Configuration file is corrupt, we cannot remove the plug-in. */
    }// try
  }// removeConfiguration(PlugInInfo, String)

  /**
   * Removes the pictures used by the given plug-in.
   *
   * @param  info
   *       information about the plug-in whose file are removed
   *
   * @throws  FileDeletingException
   *       if a picture could not be deleted
   */
  private static void removeImages(PlugInInfo info)
    throws FileDeletingException {
    String basePath = String.format(PATH_EXTENSION,
        PLUG_IN_RESOURCES_FOLDER, info.getId()) ;
    String[] pictures = new String[]{
        FORMAT_GRAY_ICON_FILE_NAME,
        FORMAT_ICON_FILE_NAME,
        FORMAT_MINI_FILE_NAME
    } ;
    for(String picture : pictures) {
      File pictureFile = new File(
          String.format(picture, basePath)) ;
      boolean deleted ;
      try {
        deleted = pictureFile.delete() ;
      } catch (SecurityException se) {
        deleted = false;
      }// try
     
      if (!deleted) {
        throw new FileDeletingException(pictureFile) ;
      }// if
    }// for
  }// removeImages(PlugInInfo)

  /**
   * Removes the pictures used by the given plug-in without throwing
   * an exception if one file could not be deleted.
   *
   * @param  info
   *       information about the plug-in whose file are removed
   */
  private static void forceRemoveImages(PlugInInfo info) {
    String basePath = String.format(PATH_EXTENSION,
        PLUG_IN_RESOURCES_FOLDER, info.getId()) ;
    String[] pictures = new String[]{
        FORMAT_GRAY_ICON_FILE_NAME,
        FORMAT_ICON_FILE_NAME,
        FORMAT_MINI_FILE_NAME
    } ;
    for(String picture : pictures) {
      File pictureFile = new File(
          String.format(picture, basePath)) ;
      try {
        pictureFile.delete() ;
      } catch (SecurityException se) {
        /* Ignore exception. */
      }// try
    }// for
  }// removeImages(PlugInInfo)
 
  /**
   * Copies the pictures of the plug-in into the resource folder.
   *
   * <p>The copying is achieved by calling the {@code copyPicture(String)} function
   * from the "main" class of the given plug-in.
   *
   * @param   info
   *       information about the plug-in whose pictures should be copied
   *
   * @throws  InvalidPlugInException
   *       if the given plug-in is invalid
   *
   * @see    PlugInInfo
   * @see    PlugIn#copyPicture(String)
   */
  private void copyImages(PlugInInfo info) throws InvalidPlugInException {
    ClassLoader loader = null ;
    if (info.isAnalyzer()) {
      loader = loadPlugIn(info, this.analyzerFolder) ;
    } else {
      loader = loadPlugIn(info, this.generatorFolder) ;
    }// if
    try {
      loader.loadClass(info.getPlugInClass()) ;
      Class<?> plugInClass = Class.forName(info.getPlugInClass(), true, loader) ;
      Class<? extends PlugIn> plClass = plugInClass.asSubclass(PlugIn.class) ;
      Constructor<? extends PlugIn> constructor = plClass.getConstructor() ;
      PlugIn plugIn = constructor.newInstance() ;
      plugIn.copyPicture(this.ressourceFolder) ;
    } catch (Exception e) {
      /*=== Possible exceptions ===
       * - ClassNotFoundException
       * - IllegalAccessException
       * - IllegalArgumentException
       * - InstantiationException
       * - InvocationTargetException
       * - NoSuchMethodException
       * - SecurityException
       */
      throw new InvalidPlugInException(info, e) ;
    }// try
  }// copyImages(ZipFile)

  /**
   * Returns an instance of {@code ClassLoader} containing the classes of the
   * loaded plug-in.
   *
   * @param   info
   *       information about the loaded plug-in
   *
   * @param   parentFolder
   *       folder containing the java archive of the loaded plug-in
   *
   * @return  A {@code ClassLoader} which can be used to instantiate the "main"
   *       class from the loaded plug-in
   *
   * @throws  InvalidPlugInException
   *       if the given plug-in is invalid
   */
  public static ClassLoader loadPlugIn(PlugInInfo info, String parentFolder)
      throws InvalidPlugInException {
    String jarPath = String.format(JAR_FILE, parentFolder, info.getJarName()) ;
    try {
      ClassLoader loader = URLClassLoader.newInstance(
          new URL[] { new File(jarPath).toURI().toURL() }
          ) ;
      return loader ;
    } catch (MalformedURLException e) {
      throw new InvalidPlugInException(info) ;
    }// try
  }// loadPlugIn(PlugInInfo, String, String)

}// class IOPlugIn
TOP

Related Classes of kameleon.util.IOPlugIn

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.