diff options
Diffstat (limited to 'gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java')
-rw-r--r-- | gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java | 348 |
1 files changed, 0 insertions, 348 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java deleted file mode 100644 index 863ef3f197..0000000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright (C) 2014 The Android Open Source Project -// -// 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.google.gerrit.server.plugins; - -import static com.google.common.base.MoreObjects.firstNonNull; -import static com.google.common.collect.Iterables.transform; - -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ListMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.MultimapBuilder; -import java.io.IOException; -import java.io.InputStream; -import java.lang.annotation.Annotation; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.jar.Attributes; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.jar.Manifest; -import org.eclipse.jgit.util.IO; -import org.objectweb.asm.AnnotationVisitor; -import org.objectweb.asm.Attribute; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.FieldVisitor; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class JarScanner implements PluginContentScanner, AutoCloseable { - private static final Logger log = LoggerFactory.getLogger(JarScanner.class); - private static final int SKIP_ALL = - ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; - private final JarFile jarFile; - - public JarScanner(Path src) throws IOException { - this.jarFile = new JarFile(src.toFile()); - } - - @Override - public Map<Class<? extends Annotation>, Iterable<ExtensionMetaData>> scan( - String pluginName, Iterable<Class<? extends Annotation>> annotations) - throws InvalidPluginException { - Set<String> descriptors = new HashSet<>(); - ListMultimap<String, JarScanner.ClassData> rawMap = - MultimapBuilder.hashKeys().arrayListValues().build(); - Map<Class<? extends Annotation>, String> classObjToClassDescr = new HashMap<>(); - - for (Class<? extends Annotation> annotation : annotations) { - String descriptor = Type.getType(annotation).getDescriptor(); - descriptors.add(descriptor); - classObjToClassDescr.put(annotation, descriptor); - } - - Enumeration<JarEntry> e = jarFile.entries(); - while (e.hasMoreElements()) { - JarEntry entry = e.nextElement(); - if (skip(entry)) { - continue; - } - - ClassData def = new ClassData(descriptors); - try { - new ClassReader(read(jarFile, entry)).accept(def, SKIP_ALL); - } catch (IOException err) { - throw new InvalidPluginException("Cannot auto-register", err); - } catch (RuntimeException err) { - log.warn( - "Plugin {} has invalid class file {} inside of {}", - pluginName, - entry.getName(), - jarFile.getName(), - err); - continue; - } - - if (!Strings.isNullOrEmpty(def.annotationName)) { - if (def.isConcrete()) { - rawMap.put(def.annotationName, def); - } else { - log.warn( - "Plugin {} tries to @{}(\"{}\") abstract class {}", - pluginName, - def.annotationName, - def.annotationValue, - def.className); - } - } - } - - ImmutableMap.Builder<Class<? extends Annotation>, Iterable<ExtensionMetaData>> result = - ImmutableMap.builder(); - - for (Class<? extends Annotation> annotoation : annotations) { - String descr = classObjToClassDescr.get(annotoation); - Collection<ClassData> discoverdData = rawMap.get(descr); - Collection<ClassData> values = firstNonNull(discoverdData, Collections.<ClassData>emptySet()); - - result.put( - annotoation, - transform(values, cd -> new ExtensionMetaData(cd.className, cd.annotationValue))); - } - - return result.build(); - } - - public List<String> findSubClassesOf(Class<?> superClass) throws IOException { - return findSubClassesOf(superClass.getName()); - } - - @Override - public void close() throws IOException { - jarFile.close(); - } - - private List<String> findSubClassesOf(String superClass) throws IOException { - String name = superClass.replace('.', '/'); - - List<String> classes = new ArrayList<>(); - Enumeration<JarEntry> e = jarFile.entries(); - while (e.hasMoreElements()) { - JarEntry entry = e.nextElement(); - if (skip(entry)) { - continue; - } - - ClassData def = new ClassData(Collections.<String>emptySet()); - try { - new ClassReader(read(jarFile, entry)).accept(def, SKIP_ALL); - } catch (RuntimeException err) { - log.warn("Jar {} has invalid class file {}", jarFile.getName(), entry.getName(), err); - continue; - } - - if (name.equals(def.superName)) { - classes.addAll(findSubClassesOf(def.className)); - if (def.isConcrete()) { - classes.add(def.className); - } - } - } - - return classes; - } - - private static boolean skip(JarEntry entry) { - if (!entry.getName().endsWith(".class")) { - return true; // Avoid non-class resources. - } - if (entry.getSize() <= 0) { - return true; // Directories have 0 size. - } - if (entry.getSize() >= 1024 * 1024) { - return true; // Do not scan huge class files. - } - return false; - } - - private static byte[] read(JarFile jarFile, JarEntry entry) throws IOException { - byte[] data = new byte[(int) entry.getSize()]; - try (InputStream in = jarFile.getInputStream(entry)) { - IO.readFully(in, data, 0, data.length); - } - return data; - } - - public static class ClassData extends ClassVisitor { - int access; - String className; - String superName; - String annotationName; - String annotationValue; - String[] interfaces; - Collection<String> exports; - - private ClassData(Collection<String> exports) { - super(Opcodes.ASM5); - this.exports = exports; - } - - boolean isConcrete() { - return (access & Opcodes.ACC_ABSTRACT) == 0 && (access & Opcodes.ACC_INTERFACE) == 0; - } - - @Override - public void visit( - int version, - int access, - String name, - String signature, - String superName, - String[] interfaces) { - this.className = Type.getObjectType(name).getClassName(); - this.access = access; - this.superName = superName; - } - - @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - if (!visible) { - return null; - } - Optional<String> found = exports.stream().filter(x -> x.equals(desc)).findAny(); - if (found.isPresent()) { - annotationName = desc; - return new AbstractAnnotationVisitor() { - @Override - public void visit(String name, Object value) { - annotationValue = (String) value; - } - }; - } - return null; - } - - @Override - public void visitSource(String arg0, String arg1) {} - - @Override - public void visitOuterClass(String arg0, String arg1, String arg2) {} - - @Override - public MethodVisitor visitMethod( - int arg0, String arg1, String arg2, String arg3, String[] arg4) { - return null; - } - - @Override - public void visitInnerClass(String arg0, String arg1, String arg2, int arg3) {} - - @Override - public FieldVisitor visitField(int arg0, String arg1, String arg2, String arg3, Object arg4) { - return null; - } - - @Override - public void visitEnd() {} - - @Override - public void visitAttribute(Attribute arg0) {} - } - - private abstract static class AbstractAnnotationVisitor extends AnnotationVisitor { - AbstractAnnotationVisitor() { - super(Opcodes.ASM5); - } - - @Override - public AnnotationVisitor visitAnnotation(String arg0, String arg1) { - return null; - } - - @Override - public AnnotationVisitor visitArray(String arg0) { - return null; - } - - @Override - public void visitEnum(String arg0, String arg1, String arg2) {} - - @Override - public void visitEnd() {} - } - - @Override - public Optional<PluginEntry> getEntry(String resourcePath) throws IOException { - JarEntry jarEntry = jarFile.getJarEntry(resourcePath); - if (jarEntry == null || jarEntry.getSize() == 0) { - return Optional.empty(); - } - - return Optional.of(resourceOf(jarEntry)); - } - - @Override - public Enumeration<PluginEntry> entries() { - return Collections.enumeration( - Lists.transform( - Collections.list(jarFile.entries()), - jarEntry -> { - try { - return resourceOf(jarEntry); - } catch (IOException e) { - throw new IllegalArgumentException( - "Cannot convert jar entry " + jarEntry + " to a resource", e); - } - })); - } - - @Override - public InputStream getInputStream(PluginEntry entry) throws IOException { - return jarFile.getInputStream(jarFile.getEntry(entry.getName())); - } - - @Override - public Manifest getManifest() throws IOException { - return jarFile.getManifest(); - } - - private PluginEntry resourceOf(JarEntry jarEntry) throws IOException { - return new PluginEntry( - jarEntry.getName(), - jarEntry.getTime(), - Optional.of(jarEntry.getSize()), - attributesOf(jarEntry)); - } - - private Map<Object, String> attributesOf(JarEntry jarEntry) throws IOException { - Attributes attributes = jarEntry.getAttributes(); - if (attributes == null) { - return Collections.emptyMap(); - } - return Maps.transformEntries( - attributes, - new Maps.EntryTransformer<Object, Object, String>() { - @Override - public String transformEntry(Object key, Object value) { - return (String) value; - } - }); - } -} |