001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.weaver.privilizer;
020
021import java.io.InputStream;
022import java.lang.annotation.ElementType;
023import java.util.ArrayList;
024import java.util.List;
025
026import org.apache.commons.io.IOUtils;
027import org.apache.commons.weaver.model.ScanRequest;
028import org.apache.commons.weaver.model.Scanner;
029import org.apache.commons.weaver.model.WeavableClass;
030import org.apache.commons.weaver.model.WeaveEnvironment;
031import org.apache.commons.weaver.model.WeaveInterest;
032import org.apache.commons.weaver.spi.Cleaner;
033import org.objectweb.asm.ClassReader;
034import org.objectweb.asm.ClassVisitor;
035import org.objectweb.asm.Opcodes;
036
037/**
038 * Removes classes privilized with a different policy.
039 */
040public class PrivilizerCleaner implements Cleaner {
041
042    @Override
043    public boolean clean(final WeaveEnvironment environment, final Scanner scanner) {
044        final Privilizer privilizer = new Privilizer(environment);
045
046        final List<String> toDelete = new ArrayList<String>();
047
048        final ScanRequest scanRequest = new ScanRequest().add(WeaveInterest.of(Privilized.class, ElementType.TYPE));
049
050        environment.debug("Cleaning classes privilized with policy other than %s", privilizer.policy);
051        for (final WeavableClass<?> weavableClass : scanner.scan(scanRequest).getClasses().with(Privilized.class)) {
052            final Policy privilizedPolicy = Policy.valueOf(weavableClass.getAnnotation(Privilized.class).value());
053            if (privilizedPolicy == privilizer.policy) {
054                continue;
055            }
056            final String className = weavableClass.getTarget().getName();
057            environment.debug("Class %s privilized with %s; deleting.", className, privilizedPolicy);
058
059            InputStream bytecode = null;
060            try {
061                bytecode = privilizer.env.getClassfile(className).getInputStream();
062                final ClassReader classReader = new ClassReader(bytecode);
063                classReader.accept(new ClassVisitor(Opcodes.ASM5) {
064                    @Override
065                    @SuppressWarnings("PMD.UseVarargs") //overridden method
066                    public void visit(final int version, final int access, final String name, final String signature,
067                        final String superName, final String[] interfaces) {
068                        toDelete.add(name);
069                    }
070
071                    @Override
072                    public void visitInnerClass(final String name, final String outerName, final String innerName,
073                        final int access) {
074                        if (toDelete.contains(outerName)) {
075                            toDelete.add(name);
076                        }
077                    }
078                }, ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES);
079            } catch (final Exception e) {
080                throw new RuntimeException(e);
081            } finally {
082                IOUtils.closeQuietly(bytecode);
083            }
084        }
085        boolean result = false;
086        for (final String className : toDelete) {
087            final String resourcePath = toResourcePath(className);
088            final boolean success = environment.deleteResource(resourcePath);
089            environment.debug("Deletion of resource %s was %ssuccessful.", resourcePath, success ? "" : "un");
090            result |= success;
091        }
092        return result;
093    }
094
095    private static String toResourcePath(final String className) {
096        return className.replace('.', '/') + ".class";
097    }
098
099}