/*
* Copyright ( c ) 2013 , 2016 , Oracle and / or its affiliates . All rights reserved .
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER .
*
* This code is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 only , as
* published by the Free Software Foundation .
*
* This code 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 General Public License
* version 2 for more details ( a copy is included in the LICENSE file that
* accompanied this code ) .
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work ; if not , write to the Free Software Foundation ,
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA .
*
* Please contact Oracle , 500 Oracle Parkway , Redwood Shores , CA 94065 USA
* or visit www . oracle . com if you need additional information or have any
* questions .
*/
package toolbox;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.tools.FileObject;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import static toolbox.ToolBox.currDir;
/**
* A task to configure and run the jar file utility .
*/
public class JarTask extends AbstractTask<JarTask> {
private Path jar;
private Manifest manifest;
private String classpath;
private String mainClass;
private Path baseDir;
private List<Path> paths;
private Set<FileObject> fileObjects;
/**
* Creates a task to write jar files , using API mode .
* @ param toolBox the { @ code ToolBox } to use
*/
public JarTask(ToolBox toolBox) {
super (toolBox, Task.Mode.API);
paths = Collections.emptyList();
fileObjects = new LinkedHashSet<>();
}
/**
* Creates a JarTask for use with a given jar file .
* @ param toolBox the { @ code ToolBox } to use
* @ param path the file
*/
public JarTask(ToolBox toolBox, String path) {
this (toolBox);
jar = Paths.get(path);
}
/**
* Creates a JarTask for use with a given jar file .
* @ param toolBox the { @ code ToolBox } to use
* @ param path the file
*/
public JarTask(ToolBox toolBox, Path path) {
this (toolBox);
jar = path;
}
/**
* Sets a manifest for the jar file .
* @ param manifest the manifest
* @ return this task object
*/
public JarTask manifest(Manifest manifest) {
this .manifest = manifest;
return this ;
}
/**
* Sets a manifest for the jar file .
* @ param manifest a string containing the contents of the manifest
* @ return this task object
* @ throws IOException if there is a problem creating the manifest
*/
public JarTask manifest(String manifest) throws IOException {
this .manifest = new Manifest(new ByteArrayInputStream(manifest.getBytes()));
return this ;
}
/**
* Sets the classpath to be written to the { @ code Class - Path }
* entry in the manifest .
* @ param classpath the classpath
* @ return this task object
*/
public JarTask classpath(String classpath) {
this .classpath = classpath;
return this ;
}
/**
* Sets the class to be written to the { @ code Main - Class }
* entry in the manifest . .
* @ param mainClass the name of the main class
* @ return this task object
*/
public JarTask mainClass(String mainClass) {
this .mainClass = mainClass;
return this ;
}
/**
* Sets the base directory for files to be written into the jar file .
* @ param baseDir the base directory
* @ return this task object
*/
public JarTask baseDir(String baseDir) {
this .baseDir = Paths.get(baseDir);
return this ;
}
/**
* Sets the base directory for files to be written into the jar file .
* @ param baseDir the base directory
* @ return this task object
*/
public JarTask baseDir(Path baseDir) {
this .baseDir = baseDir;
return this ;
}
/**
* Sets the files to be written into the jar file .
* @ param files the files
* @ return this task object
*/
public JarTask files(String... files) {
this .paths = Stream.of(files)
.map(file -> Paths.get(file))
.collect(Collectors.toList());
return this ;
}
/**
* Adds a set of file objects to be written into the jar file , by copying them
* from a Location in a JavaFileManager .
* The file objects to be written are specified by a series of paths ;
* each path can be in one of the following forms :
* < ul >
* < li > The name of a class . For example , java . lang . Object .
* In this case , the corresponding . class file will be written to the jar file .
* < li > the name of a package followed by { @ code . * } . For example , { @ code java . lang . * } .
* In this case , all the class files in the specified package will be written to
* the jar file .
* < li > the name of a package followed by { @ code . * * } . For example , { @ code java . lang . * * } .
* In this case , all the class files in the specified package , and any subpackages
* will be written to the jar file .
* < / ul >
*
* @ param fm the file manager in which to find the file objects
* @ param l the location in which to find the file objects
* @ param paths the paths specifying the file objects to be copied
* @ return this task object
* @ throws IOException if errors occur while determining the set of file objects
*/
public JarTask files(JavaFileManager fm, JavaFileManager.Location l, String... paths)
throws IOException {
for (String p : paths) {
if (p.endsWith(".**" ))
addPackage(fm, l, p.substring(0 , p.length() - 3 ), true );
else if (p.endsWith(".*" ))
addPackage(fm, l, p.substring(0 , p.length() - 2 ), false );
else
addFile(fm, l, p);
}
return this ;
}
private void addPackage(JavaFileManager fm, JavaFileManager.Location l, String pkg, boolean recurse)
throws IOException {
for (JavaFileObject fo : fm.list(l, pkg, EnumSet.allOf(JavaFileObject.Kind.class ), recurse)) {
fileObjects.add(fo);
}
}
private void addFile(JavaFileManager fm, JavaFileManager.Location l, String path) throws IOException {
JavaFileObject fo = fm.getJavaFileForInput(l, path, JavaFileObject.Kind.CLASS );
fileObjects.add(fo);
}
/**
* Provides limited jar command - like functionality .
* The supported commands are :
* < ul >
* < li > jar cf jarfile - C dir files . . .
* < li > jar cfm jarfile manifestfile - C dir files . . .
* < / ul >
* Any values specified by other configuration methods will be ignored .
* @ param args arguments in the style of those for the jar command
* @ return a Result object containing the results of running the task
*/
public Task.Result run(String... args) {
if (args.length < 2 )
throw new IllegalArgumentException();
ListIterator<String> iter = Arrays.asList(args).listIterator();
String first = iter.next();
switch (first) {
case "cf" :
jar = Paths.get(iter.next());
break ;
case "cfm" :
jar = Paths.get(iter.next());
try (InputStream in = Files.newInputStream(Paths.get(iter.next()))) {
manifest = new Manifest(in);
} catch (IOException e) {
throw new IOError(e);
}
break ;
}
if (iter.hasNext()) {
if (iter.next().equals("-C" ))
baseDir = Paths.get(iter.next());
else
iter.previous();
}
paths = new ArrayList<>();
while (iter.hasNext())
paths.add(Paths.get(iter.next()));
return run();
}
/**
* { @ inheritDoc }
* @ return the name " jar "
*/
@Override
public String name() {
return "jar" ;
}
/**
* Creates a jar file with the arguments as currently configured .
* @ return a Result object indicating the outcome of the compilation
* and the content of any output written to stdout , stderr , or the
* main stream by the compiler .
* @ throws TaskError if the outcome of the task is not as expected .
*/
@Override
public Task.Result run() {
Manifest m = (manifest == null ) ? new Manifest() : manifest;
Attributes mainAttrs = m.getMainAttributes();
if (mainClass != null )
mainAttrs.put(Attributes.Name.MAIN_CLASS, mainClass);
if (classpath != null )
mainAttrs.put(Attributes.Name.CLASS_PATH, classpath);
AbstractTask.StreamOutput sysOut = new AbstractTask.StreamOutput(System.out, System::setOut);
AbstractTask.StreamOutput sysErr = new AbstractTask.StreamOutput(System.err, System::setErr);
Map<Task.OutputKind, String> outputMap = new HashMap<>();
try (OutputStream os = Files.newOutputStream(jar);
JarOutputStream jos = openJar(os, m)) {
writeFiles(jos);
writeFileObjects(jos);
} catch (IOException e) {
error("Exception while opening " + jar, e);
} finally {
outputMap.put(Task.OutputKind.STDOUT, sysOut.close());
outputMap.put(Task.OutputKind.STDERR, sysErr.close());
}
return checkExit(new Task.Result(toolBox, this , (errors == 0 ) ? 0 : 1 , outputMap));
}
private JarOutputStream openJar(OutputStream os, Manifest m) throws IOException {
if (m == null || m.getMainAttributes().isEmpty() && m.getEntries().isEmpty()) {
return new JarOutputStream(os);
} else {
if (m.getMainAttributes().get(Attributes.Name.MANIFEST_VERSION) == null )
m.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0" );
return new JarOutputStream(os, m);
}
}
private void writeFiles(JarOutputStream jos) throws IOException {
Path base = (baseDir == null ) ? currDir : baseDir;
for (Path path : paths) {
Files.walkFileTree(base.resolve(path), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
try {
String p = base.relativize(file)
.normalize()
.toString()
.replace(File.separatorChar, '/' );
JarEntry e = new JarEntry(p);
jos.putNextEntry(e);
try {
jos.write(Files.readAllBytes(file));
} finally {
jos.closeEntry();
}
return FileVisitResult.CONTINUE ;
} catch (IOException e) {
error("Exception while adding " + file + " to jar file" , e);
return FileVisitResult.TERMINATE;
}
}
});
}
}
private void writeFileObjects(JarOutputStream jos) throws IOException {
for (FileObject fo : fileObjects) {
String p = guessPath(fo);
JarEntry e = new JarEntry(p);
jos.putNextEntry(e);
try {
byte [] buf = new byte [1024 ];
try (BufferedInputStream in = new BufferedInputStream(fo.openInputStream())) {
int n;
while ((n = in.read(buf)) > 0 )
jos.write(buf, 0 , n);
} catch (IOException ex) {
error("Exception while adding " + fo.getName() + " to jar file" , ex);
}
} finally {
jos.closeEntry();
}
}
}
/*
* A jar : URL is of the form jar : URL ! / < entry > where URL is a URL for the . jar file itself .
* In Symbol files ( i . e . ct . sym ) the underlying entry is prefixed META - INF / sym / < base > .
*/
private final Pattern jarEntry = Pattern.compile(".*!/(?:META-INF/sym/[^/]+/)?(.*)" );
/*
* A jrt : URL is of the form jrt : / < module > / < package > / < file >
*/
private final Pattern jrtEntry = Pattern.compile("/([^/]+)/(.*)" );
/*
* A file : URL is of the form file : / path / to / { modules , patches } / < module > / < package > / < file >
*/
private final Pattern fileEntry = Pattern.compile(".*/(?:modules|patches)/([^/]+)/(.*)" );
private String guessPath(FileObject fo) {
URI u = fo.toUri();
switch (u.getScheme()) {
case "jar" : {
Matcher m = jarEntry.matcher(u.getSchemeSpecificPart());
if (m.matches()) {
return m.group(1 );
}
break ;
}
case "jrt" : {
Matcher m = jrtEntry.matcher(u.getSchemeSpecificPart());
if (m.matches()) {
return m.group(2 );
}
break ;
}
case "file" : {
Matcher m = fileEntry.matcher(u.getSchemeSpecificPart());
if (m.matches()) {
return m.group(2 );
}
break ;
}
}
throw new IllegalArgumentException(fo.getName() + "--" + fo.toUri());
}
private void error(String message, Throwable t) {
toolBox.out.println("Error: " + message + ": " + t);
errors++;
}
private int errors;
}
Messung V0.5 in Prozent C=88 H=92 G=89
¤ Dauer der Verarbeitung: 0.9 Sekunden
¤
*© Formatika GbR, Deutschland