org.evosuite.runtime.util.JarPathing Maven / Gradle / Ivy
/**
* Copyright (C) 2010-2017 Gordon Fraser, Andrea Arcuri and EvoSuite
* contributors
*
* This file is part of EvoSuite.
*
* EvoSuite is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* EvoSuite is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with EvoSuite. If not, see .
*/
package org.evosuite.runtime.util;
import org.evosuite.runtime.RuntimeSettings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
/**
* Created by Andrea Arcuri on 10/11/15.
*/
public class JarPathing {
private static Logger logger = LoggerFactory.getLogger(JarPathing.class);
private static final String PATHING_JAR_PREFIX = "EvoSuite_pathingJar";
public static boolean containsAPathingJar(String sequence) {
for(String token : sequence.split(File.pathSeparator)) {
if(isPathingJar(token)) {
return true;
}
}
return false;
}
public static boolean isPathingJar(String path) throws IllegalArgumentException {
if(path.contains(File.pathSeparator)) {
throw new IllegalArgumentException("Multiple elements in path: "+path);
}
return path != null && path.contains(PATHING_JAR_PREFIX) && path.endsWith(".jar");
}
public static String expandPathingJars(String sequence) {
List list = new ArrayList<>();
for(String token : sequence.split(File.pathSeparator)) {
if(isPathingJar(token)) {
list.add(extractCPFromPathingJar(token));
} else {
list.add(token);
}
}
return String.join(File.pathSeparator, list);
}
public static String extractCPFromPathingJar(String pathingJar) throws IllegalArgumentException{
Inputs.checkNull(pathingJar);
if(!isPathingJar(pathingJar)) {
throw new IllegalArgumentException("Invalid pathing jar name: "+pathingJar);
}
File jar = new File(pathingJar);
if(!jar.exists()) {
throw new IllegalArgumentException("Pathing jar does not exist: "+pathingJar);
}
try (JarInputStream in = new JarInputStream(new FileInputStream(jar))) {
Manifest m = in.getManifest();
String escapedCP = m.getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
List list = new ArrayList<>();
for(String token : escapedCP.split(" ")) {
File file = new File(token.replace("%20"," "));
if(!file.exists()) {
//this should never happen, unless bug in EvoSuite
throw new IllegalStateException("Pathing jar "+pathingJar+" refers to non-existing entry " + token);
}
if(isPathingJar(file.getAbsolutePath())) {
throw new IllegalArgumentException("Pathing jar "+pathingJar+" contains the pathing jar "+file.getAbsolutePath());
}
list.add(file.getAbsolutePath());
}
return String.join(File.pathSeparator, list);
} catch (IOException e) {
logger.error(e.toString(),e);
return pathingJar;
}
}
/**
* Create a jar file in the tmp directory having the given {@code classpath} in the
* manifest, and return the path to such jar. This is done to avoid issues in
* Windows where cannot have too long classpaths
*
* @param classpath
* @return
*/
public static String createJarPathing(String classpath) {
logger.debug("Going to create jar pathing for: {}", classpath);
List elements = new ArrayList<>();
elements.addAll(Arrays.asList(classpath.split(File.pathSeparator)));
StringBuffer escaped = new StringBuffer();
while(!elements.isEmpty()) {
String element = elements.remove(0);
try {
//be sure the classpath element is absolute
File file = new File(element);
element = file.getAbsolutePath();
if(!file.exists()) {
logger.warn("Classpath entry does not exist: {}", element);
continue;
}
if(isPathingJar(element)) {
elements.addAll(Arrays.asList(extractCPFromPathingJar(element).split(File.pathSeparator)));
continue;
}
/*
as the path separator in the manifest is just spaces " ", we need
to escape the paths to URL to avoid issues in Windows where path might
have spaces...
*/
element = element.replace("\\","/");
element = element.replace(" ","%20");
if(!element.startsWith("/")) {
element = "/" + element;
}
if(!element.endsWith(".jar")) {
//that means it is a folder. we need to make sure it does end with a "/"
if(!element.endsWith("/")) {
element += "/";
}
}
escaped.append(element+ " ");
//escaped.append(URLEncoder.encode(element,"UTF-8")+ " ");
} catch (Exception e) {
logger.error("Problem when encoding '"+element+"': "+e.toString());
return classpath;
}
}
String jarLocation = null;
try {
File tmp = File.createTempFile(PATHING_JAR_PREFIX,".jar");
tmp.deleteOnExit();
jarLocation = tmp.getAbsolutePath();
Manifest m = new Manifest();
m.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
m.getMainAttributes().put(Attributes.Name.CLASS_PATH, escaped.toString());
JarOutputStream out = new JarOutputStream(new FileOutputStream(tmp), m);
out.flush();
out.close();
if(!RuntimeSettings.isRunningASystemTest) {
//the location is likely non-deterministic
logger.info("Created jar path at {} with CP: {}", jarLocation, escaped.toString());
}
} catch (Exception e) {
logger.error("Cannot create pathing jar: "+e.toString());
return classpath;
}
return jarLocation;
}
}