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.ASM4) {
064                    @Override
065                    public void visit(final int version, final int access, final String name, final String signature,
066                        final String superName, final String[] interfaces) {
067                        toDelete.add(name);
068                    }
069
070                    @Override
071                    public void visitInnerClass(final String name, final String outerName, final String innerName,
072                        final int access) {
073                        if (toDelete.contains(outerName)) {
074                            toDelete.add(name);
075                        }
076                    }
077                }, ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES);
078            } catch (final Exception e) {
079                throw new RuntimeException(e);
080            } finally {
081                IOUtils.closeQuietly(bytecode);
082            }
083        }
084        boolean result = false;
085        for (final String className : toDelete) {
086            final String resourcePath = toResourcePath(className);
087            final boolean success = environment.deleteResource(resourcePath);
088            environment.debug("Deletion of resource %s was %ssuccessful.", resourcePath, success ? "" : "un");
089            result |= success;
090        }
091        return result;
092    }
093
094    private static String toResourcePath(final String className) {
095        return className.replace('.', '/') + ".class";
096    }
097
098}