Package gov.nasa.jpf.conformanceChecker.providers

Source Code of gov.nasa.jpf.conformanceChecker.providers.StandardClassProvider

//
// Copyright (C) 2012 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA).  All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3.  The NOSA has been approved by the Open Source
// Initiative.  See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.conformanceChecker.providers;

import static gov.nasa.jpf.conformanceChecker.util.MaybeVersion.*;
import gov.nasa.jpf.Config;
import gov.nasa.jpf.JPF;
import gov.nasa.jpf.classfile.ClassFile;
import gov.nasa.jpf.classfile.ClassFileException;
import gov.nasa.jpf.conformanceChecker.util.MaybeVersion;
import gov.nasa.jpf.jvm.ClassInfo;
import gov.nasa.jpf.jvm.JVM;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
* This class gives access to standard library classes located in
* system jar files.
*
* The entry point of this class is method {@link #loadClassInfo(int, String)}.
* With this method a {@link ClassInfo} object is loaded for the
* specified java version of the class.
*
* This class uses the following properties in the project's
* jpf configuration file:
*
*   - conformance-checker.java5path
*   - conformance-checker.java6path
*   - conformance-checker.java7path
*   - conformance-checker.libJars
*  
* that list the path to the directory containing the system
* library jars for java 5, 6 ant 7 respectively and a list
* of third party libraries.
*
* ClassInfo objects returned by this class are not registered
* to the JPF JVM.
*
* @author Matteo Ceccarello <matteo.ceccarello AT gmail.com>
*
*/
public class StandardClassProvider {
 
  static {
    // FIXME this is the hack needed to have static fields in JVM class
    // initialized, so that ClassInfo does not throw NullPointer
    // when initialized.
    if(JVM.getVM() == null)
      new JVM(null, new Config(new String[0]));
  }
 
  @SuppressWarnings("serial")
  public static class UnsupportedJavaVersionException extends RuntimeException {
    public UnsupportedJavaVersionException() {
      super();
    }
    public UnsupportedJavaVersionException(String message){
      super(message);
    }
  }

  public static final String JAVA_5_PATH_PROP = "conformance-checker.java5path";
  public static final String JAVA_6_PATH_PROP = "conformance-checker.java6path";
  public static final String JAVA_7_PATH_PROP = "conformance-checker.java7path";
 
  public static final String EXT_LIBS_PROP = "conformance-checker.libJars";
 
  public static final Logger logger = JPF.getLogger(StandardClassProvider.class.getName());
 
  private JarFile[] java5jars = {};
  private JarFile[] java6jars = {};
  private JarFile[] java7jars = {};
 
  private JarFile[] extLibsJars = {};
 
  /* caches */
 
  private Map<String, ClassInfo> java5cache = new HashMap<String, ClassInfo>();
  private Map<String, ClassInfo> java6cache = new HashMap<String, ClassInfo>();
  private Map<String, ClassInfo> java7cache = new HashMap<String, ClassInfo>();
 
  private Map<String, ClassInfo> extLibscache = new HashMap<String, ClassInfo>();
 

  /**
   * Builds a new {@link StandardClassProvider} that tries
   * to guess the location of all available system
   * libraries, for all installed Java versions.
   *
   * If there is a jpf config file available, then
   * properties are read from there.
   */
  public StandardClassProvider() {
    this(new Config(new String[]{}));
  }
 
  /**
   * Builds a new {@link StandardClassProvider} that tries
   * to guess the location of all available system
   * libraries, for all installed Java versions.
   *
   * If the given {@link Config} object contains
   * the appropriate options (see the class description)
   * then java libraries are loaded from that paths.
   */
  public StandardClassProvider(Config config) {
    List<Short> remainingVersions = loadConfiguredVersions(config);
    loadOtherVersionsJars(remainingVersions);
    loadThirdPartyLibs(config);
  }

  /**
   * Loads a {@link ClassInfo} object from the given java version.
   *
   * If the version is is {@link Maybe#NOTHING}, then the method
   * looks into the external library array.
   *
   * If the class is not found, this method will return null. This may be
   * caused by IO exceptions, {@link ClassFileException}s and other error
   * conditions that generally means that the class is not located in the
   * given jars. This should be interpreted as the fact that the requested
   * class is not present in the java version requested.
   *
   * @param javaVersion the java version desired for the class
   * @param className the class to look for
   * @return a {@link ClassInfo} object representing the requested class if
   *         the class is found in the requested version, null otherwise.
   */
  public ClassInfo loadClassInfo(MaybeVersion javaVersion, String className){
    ClassInfo cached = null;
    if(javaVersion.isNothing()) {
      cached = extLibscache.get(className);
      if(cached != null)
        return cached;
      cached = loadClassFromJarFiles(extLibsJars, className);
      extLibscache.put(className, cached);
      return cached;
    }
    switch(javaVersion.getJust()) {
    case 5:
      cached = java5cache.get(className);
      if(cached != null)
        return cached;
      cached = loadClassFromJarFiles(java5jars, className);
      java5cache.put(className, cached);
      return cached;
    case 6:
      cached = java6cache.get(className);
      if (cached != null)
        return cached;
      cached = loadClassFromJarFiles(java6jars, className);
      java6cache.put(className, cached);
      return cached;
    case 7:
      cached = java7cache.get(className);
      if (cached != null)
        return cached;
      cached = loadClassFromJarFiles(java7jars, className);
      java7cache.put(className, cached);
      return cached;
    default:
      throw new UnsupportedJavaVersionException("Supported Java versions are 5, 6 and 7");
    }

  }
 
  /**
   * Loads a {@link ClassInfo} object, for all java versions.
   * This method looks also into the external library paths
   *
   * If the class is not found, this method will return null. This may be
   * caused by IO exceptions, {@link ClassFileException}s and other error
   * conditions that generally means that the class is not located in the
   * given jars. This should be interpreted as the fact that the requested
   * class is not present in the java version requested.
   *
   * @param javaVersion the java version desired for the class
   * @param className the class to look for
   * @return a {@link Map} between java versions and
   *         {@link ClassInfo} objects.
   */
  public Map<MaybeVersion, ClassInfo> loadClassInfo(String className){
    Map<MaybeVersion, ClassInfo> map = new HashMap<MaybeVersion, ClassInfo>();
    ClassInfo ext = loadClassInfo(nothing(), className);
    if(ext != null) {
      map.put(nothing(), ext);
    }
    ClassInfo ver5 = loadClassInfo(just(5), className);
    if(ver5 != null)
      map.put(just(5), ver5);
    ClassInfo ver6 = loadClassInfo(just(6), className);
    if(ver6 != null)
      map.put(just(6), ver6);
    ClassInfo ver7 = loadClassInfo(just(7), className);
    if(ver7 != null)
      map.put(just(7), ver7);
    return map;
  }
 
  void loadThirdPartyLibs(Config config) {
    File[] jarPaths = config.getPathArray(EXT_LIBS_PROP);
    ArrayList<JarFile> jars = new ArrayList<JarFile>();
    for (File file : jarPaths) {
      try {
        jars.add(new JarFile(file));
      } catch (IOException e) {
        logger.warning("failed to load jar file from " +
            file.getAbsolutePath() + ", skipping. \n\t" + e.getMessage());
      }
    }
    extLibsJars = jars.toArray(extLibsJars);
  }

  /**
   * Loads the java versions explicitly specified in the
   * given config and return a list of versions to load
   * without the config help.
   *
   * If the option is configured as the empty string, then the program
   * tries to find it itself, if it is null it is skipped
   */
  List<Short> loadConfiguredVersions(Config config) {
    List<Short> remainingVersions =
        new ArrayList<Short>(Arrays.asList((short) 5, (short) 6, (short) 7));
   
    String dir = config.getString(JAVA_5_PATH_PROP);
    if(dir != null) {
      if(!"".equals(dir)){
        java5jars = loadFromDirIntoArray(dir);
        if(java5jars.length > 0){
          logger.fine("Successfully loaded Java 5 from "+dir);
          remainingVersions.remove(new Short((short) 5));
        }
      }
    } else remainingVersions.remove(new Short((short) 5)); // the version should be ignored
    dir = config.getString(JAVA_6_PATH_PROP);
    if(dir != null) {
      if(!"".equals(dir)){
        java6jars = loadFromDirIntoArray(dir);
        if(java6jars.length > 0){
          logger.fine("Successfully loaded Java 6 from "+dir);
          remainingVersions.remove(new Short((short) 6));
        }
      }
    } else remainingVersions.remove(new Short((short) 6)); // the version should be ignored
    dir = config.getString(JAVA_7_PATH_PROP);
    if(dir != null) {
      if (!"".equals(dir)){
        java7jars = loadFromDirIntoArray(dir);
        if(java7jars.length > 0){
          logger.fine("Successfully loaded Java 7 from "+dir);
          remainingVersions.remove(new Short((short) 7));
        }
      }
    } else remainingVersions.remove(new Short((short) 7)); // the version should be ignored
   
    return remainingVersions;
  }
 
  JarFile[] loadFromDirIntoArray(String dir) {
    JarFile[] jars;
    File libDir = new File(dir);
    if(libDir.exists()) {
      File[] jarFiles = libDir.listFiles(
          new FileFilter() {
            public boolean accept(File pathname) {
              return pathname.getName().endsWith(".jar");
            }
          });
      if(jarFiles.length == 0) {
        logger.warning("The directory " + dir + " does not contain" +
            "jar files");
        return new JarFile[0];
      }
      jars = new JarFile[jarFiles.length];
      for (int i = 0; i < jars.length; i++) {
        try {
          jars[i] = new JarFile(jarFiles[i]);
        } catch (IOException e) {
          logger.warning("Error loading jar file: " +
              jarFiles[i]);
        }
      }
      return jars;
    } else {
      logger.warning("The directory " + dir + " does not exists")
      return new JarFile[0];
    }
  }

  /**
   * Loads the libraries for other java version, specified by versionInUse and
   * otherVersions.
   *
   * @param versionInUseDir
   * @param versionInUse
   * @param otherVersions
   */
  void loadOtherVersionsJars(List<Short> otherVersions) {
    logger.finer("Trying to load the following java versions: "+otherVersions);
    String currentLibraryBaseDir = currentLibraryBaseDir();
    short versionInUse = detectJavaVersion();
    for (short ver : otherVersions) {
      String dir = inferLibraryBaseDir(currentLibraryBaseDir, versionInUse, ver);
      boolean success;
      switch (ver) {
      case 5:
        java5jars = loadFromDirIntoArray(dir);
        success = java5jars.length > 0;
        break;
      case 6:
        java6jars = loadFromDirIntoArray(dir);
        success = java6jars.length > 0;
        break;
      case 7:
        java7jars = loadFromDirIntoArray(dir);
        success = java7jars.length > 0;
        break;
      default:
        throw new UnsupportedJavaVersionException("Supported Java versions are 5, 6 and 7");
      }
      if(success) {
        logger.finer("Successfully loaded Java " + ver + " from " + dir);
      } else {       
        logger.warning("Failed loading Java " + ver +" library. " +
            "Maybe it is not installed on you system");       
      }
    }
  }

  /**
   * Loads a {@link ClassInfo} object from the given list of jar files.
   *
   * If the class is not found, this method will return null. This may be
   * caused by IO exceptions, {@link ClassFileException}s and other error
   * conditions that generally means that the class is not located in the
   * given jars. This should be interpreted as the fact that the requested
   * class is not present in the java version represented by the given
   * jar files.
   *
   * @param jars the jar files to look into.
   * @param className the class to look for
   * @return a {@link ClassInfo} object representing the requested class if
   *         the class is found, null otherwise.
   */
  ClassInfo loadClassFromJarFiles(JarFile[] jars, String className) {
    // FIXME: check if on Windows the file separator inside jar files
    // is / or \
    String fileName = className.replace('.', '/') + ".class";
    for (JarFile jar : jars) {
      JarEntry entry = jar.getJarEntry(fileName);
      if (entry != null) {
        try {
          logger.fine("Loaded " + className + " from jar file " + jar.getName());
          byte[] classBytes = Util.getClassBytes(jar.getInputStream(entry));
          ClassFile cf = new ClassFile(className, classBytes);
          return new UnregisteredClassInfo(cf);
        } catch (IOException e) {
          logger.warning(e.getMessage());
          return null;
        } catch (ClassFileException e) {
          logger.warning(e.getMessage());
          return null;
        }
      }
    }
    return null;
  }

  public static short detectJavaVersion() {
    String javaVersion = System.getProperty("java.version");
    String[] versionNumber = javaVersion.split("\\.");
    logger.finer("Java version currently in use is: " + javaVersion);
    return Short.parseShort(versionNumber[1]);
  }
 
  /**
   * Infers the path to the set of libraries for the
   * Java version currently in use.
   *
   * Includes the last slash in the returned value
   */
  static String currentLibraryBaseDir() {
    String aJar = System.getProperty("sun.boot.class.path").split(":")[0];
    String inferred = aJar.substring(0, aJar.lastIndexOf(File.separator)+1);
    return inferred;
  }
 
  static String inferLibraryBaseDir(String versionInUseDir, short versionInUse, short wantedVersion) {
    // FIXME in Sabayon Linux (Icedtea) this does not work.
    // Java versions are in directories like /opt/icedtea-bin-7.2.1/jre/lib/
    return versionInUseDir.replace("."+versionInUse+".", "."+wantedVersion+".");
  }
 
  /** small test main */
  public static void main(String[] args) {
    logger.setLevel(Level.ALL);
    Map<MaybeVersion, ClassInfo> classes = new StandardClassProvider().loadClassInfo("java.io.File");
    System.out.println(classes);
  }

}
TOP

Related Classes of gov.nasa.jpf.conformanceChecker.providers.StandardClassProvider

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.