Java tutorial
/* * Copyright 2015-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.facebook.buck.jvm.java; import com.facebook.buck.log.Logger; import com.facebook.buck.util.PatternsMatcher; import com.facebook.buck.zip.CustomZipEntry; import com.facebook.buck.zip.CustomZipOutputStream; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import java.io.File; import java.io.IOException; import java.util.HashSet; import java.util.Set; import java.util.concurrent.Semaphore; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import javax.tools.FileObject; import javax.tools.ForwardingJavaFileManager; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; /** * A {@link StandardJavaFileManager} that creates and writes the content of files directly into a * Jar output stream instead of writing the files to disk. */ public class JavaInMemoryFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> implements StandardJavaFileManager { private static final Logger LOG = Logger.get(JavaInMemoryFileManager.class); private CustomZipOutputStream jarOutputStream; private StandardJavaFileManager delegate; private Semaphore jarFileSemaphore = new Semaphore(1); private Set<String> directoryPaths; private Set<String> javaFileForOutputPaths; private PatternsMatcher classesToRemoveFromJar; public JavaInMemoryFileManager(StandardJavaFileManager standardManager, CustomZipOutputStream jarOutputStream, ImmutableSet<Pattern> classesToRemoveFromJar) { super(standardManager); this.delegate = standardManager; this.jarOutputStream = jarOutputStream; this.directoryPaths = new HashSet<>(); this.javaFileForOutputPaths = new HashSet<>(); this.classesToRemoveFromJar = new PatternsMatcher(classesToRemoveFromJar); } /** * Creates a ZipEntry for placing in the jar output stream. Sets the modification time to 0 for * a deterministic jar. * @param name the name of the entry * @return the zip entry for the file specified */ public static ZipEntry createEntry(String name) { CustomZipEntry entry = new CustomZipEntry(name); // We want deterministic JARs, so avoid mtimes. entry.setFakeTime(); return entry; } private static String getPath(String className) { return className.replace('.', '/'); } private static String getPath(String className, JavaFileObject.Kind kind) { return className.replace('.', '/') + kind.extension; } @Override public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { // Use the normal FileObject that writes to the disk for source files. if (kind.equals(JavaFileObject.Kind.SOURCE)) { return delegate.getJavaFileForOutput(location, className, kind, sibling); } // If the class is to be removed from the Jar create a NoOp FileObject. if (classesToRemoveFromJar.hasPatterns() && classesToRemoveFromJar.matches(className.toString())) { LOG.info("%s was excluded from the Jar because it matched a remove_classes pattern.", className.toString()); return createJavaNoOpFileObject(getPath(className, kind), kind); } // Create the directories that are part of the class name path for (int i = 0; i < className.length(); ++i) { if (className.charAt(i) == '.') { String directoryPath = getPath(className.substring(0, i + 1)); if (directoryPaths.contains(directoryPath)) { continue; } createDirectory(directoryPath); directoryPaths.add(directoryPath); } } // Return a FileObject that it will be written in the jar. return createJavaMemoryFileObject(getPath(className, kind), kind); } @Override public boolean isSameFile(FileObject a, FileObject b) { boolean aInMemoryInstance = a instanceof JavaInMemoryFileObject; boolean bInMemoryInstance = b instanceof JavaInMemoryFileObject; if (aInMemoryInstance || bInMemoryInstance) { return aInMemoryInstance && bInMemoryInstance && a.getName().equals(b.getName()); } return super.isSameFile(a, b); } @Override public Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(Iterable<? extends File> files) { return delegate.getJavaFileObjectsFromFiles(files); } @Override public Iterable<? extends JavaFileObject> getJavaFileObjects(File... files) { return delegate.getJavaFileObjects(files); } @Override public Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable<String> names) { return delegate.getJavaFileObjectsFromStrings(names); } @Override public Iterable<? extends JavaFileObject> getJavaFileObjects(String... names) { return delegate.getJavaFileObjects(names); } @Override public void setLocation(Location location, Iterable<? extends File> path) throws IOException { delegate.setLocation(location, path); } @Override public Iterable<? extends File> getLocation(Location location) { return delegate.getLocation(location); } public ImmutableSet<String> getEntries() { return ImmutableSet.copyOf(Sets.union(directoryPaths, javaFileForOutputPaths)); } private void createDirectory(String name) throws IOException { JavaFileObject fileObject = createJavaMemoryFileObject(name, JavaFileObject.Kind.OTHER); jarFileSemaphore.acquireUninterruptibly(); try { jarOutputStream.putNextEntry(createEntry(fileObject.getName())); jarOutputStream.closeEntry(); } finally { jarFileSemaphore.release(); } } private JavaFileObject createJavaMemoryFileObject(String path, JavaFileObject.Kind kind) { JavaFileObject obj = new JavaInMemoryFileObject(path, kind, jarOutputStream, jarFileSemaphore); javaFileForOutputPaths.add(obj.getName()); return obj; } private JavaFileObject createJavaNoOpFileObject(String path, JavaFileObject.Kind kind) { JavaFileObject obj = new JavaNoOpFileObject(path, kind); javaFileForOutputPaths.add(obj.getName()); return obj; } }