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.model;
020
021import java.io.IOException;
022import java.io.InputStream;
023import java.io.OutputStream;
024import java.util.Properties;
025import java.util.logging.Logger;
026
027import javax.activation.DataSource;
028
029import org.apache.commons.lang3.Validate;
030import org.apache.commons.weaver.spi.Cleaner;
031import org.apache.commons.weaver.spi.Weaver;
032
033/**
034 * Encapsulates the environment in which a {@link Weaver} or {@link Cleaner} must operate.
035 */
036public abstract class WeaveEnvironment {
037    private static final String CONTENT_TYPE = "application/octet-stream";
038
039    private class Resource implements DataSource {
040        private final String name;
041
042        Resource(final String name) {
043            this.name = name;
044        }
045
046        /**
047         * Get the content type, always "application/octet-stream".
048         * @return {@link String}
049         */
050        @Override
051        public String getContentType() {
052            return CONTENT_TYPE;
053        }
054
055        /**
056         * Get an {@link InputStream} for reading this {@link Resource}.
057         */
058        @Override
059        public InputStream getInputStream() throws IOException {
060            return classLoader.getResourceAsStream(name);
061        }
062
063        /**
064         * Get the name of this {@link Resource}.
065         * @return {@link String}
066         */
067        @Override
068        public String getName() {
069            return name;
070        }
071
072        /**
073         * Get an {@link OutputStream} for writing to this {@link Resource}.
074         * @return {@link OutputStream}
075         */
076        @Override
077        public OutputStream getOutputStream() throws IOException {
078            return WeaveEnvironment.this.getOutputStream(name);
079        }
080    }
081
082    /**
083     * ClassLoader containing scannable and weavable classes.
084     */
085    public final ClassLoader classLoader;
086
087    /**
088     * Configuration properties. By convention, any configuration property should start with its name, e.g.
089     * "privilizer".
090     */
091    public final Properties config;
092
093    private final Logger log;
094
095    /**
096     * Create a new {@link WeaveEnvironment}.
097     * @param classLoader property
098     * @param config property
099     * @param log property
100     */
101    protected WeaveEnvironment(final ClassLoader classLoader, final Properties config, final Logger log) {
102        super();
103        this.classLoader = classLoader;
104        this.config = (Properties) Validate.notNull(config, "config").clone();
105        this.log = log;
106    }
107
108    /**
109     * Handle a debug message.
110     * @param message text
111     * @param args format
112     * @see String#format(String, Object...)
113     */
114    public void debug(final String message, final Object... args) {
115        log.fine(String.format(message, args));
116    }
117
118    /**
119     * Handle a verbose message.
120     * @param message text
121     * @param args format
122     * @see String#format(String, Object...)
123     */
124    public void verbose(final String message, final Object... args) {
125        log.fine(String.format(message, args));
126    }
127
128    /**
129     * Handle a warning message.
130     * @param message text
131     * @param args format
132     * @see String#format(String, Object...)
133     */
134    public void warn(final String message, final Object... args) {
135        log.warning(String.format(message, args));
136    }
137
138    /**
139     * Handle an info message.
140     * @param message text
141     * @param args format
142     * @see String#format(String, Object...)
143     */
144    public void info(final String message, final Object... args) {
145        log.info(String.format(message, args));
146    }
147
148    /**
149     * Handle an error message.
150     * @param message text
151     * @param args format
152     * @see String#format(String, Object...)
153     */
154    public void error(final String message, final Object... args) {
155        log.severe(String.format(message, args));
156    }
157
158    /**
159     * Get a {@link DataSource} representing {@code cls}.
160     * @param cls type
161     * @return {@link DataSource}
162     */
163    public final DataSource getClassfile(final Class<?> cls) {
164        return getClassfile(cls.getName());
165    }
166
167    /**
168     * Get a {@link DataSource} for the specified class.
169     * @param classname of type
170     * @return {@link DataSource}
171     */
172    public final DataSource getClassfile(final String classname) {
173        return getResource(getResourceName(classname));
174    }
175
176    /**
177     * Get a {@link DataSource} for the specified resource.
178     * @param name of resource
179     * @return {@link DataSource}
180     */
181    public final DataSource getResource(final String name) {
182        return new Resource(name);
183    }
184
185    /**
186     * Delete the classfile for {@code cls}.
187     * @param cls type
188     * @return whether successful
189     */
190    public final boolean deleteClassfile(final Class<?> cls) {
191        return deleteClassfile(cls.getName());
192    }
193
194    /**
195     * Delete the classfile for the specified class.
196     * @param classname of type
197     * @return whether successful
198     */
199    public final boolean deleteClassfile(final String classname) {
200        return deleteResource(getResourceName(classname));
201    }
202
203    /**
204     * Delete the specified resource.
205     * @param name to delete
206     * @return whether successful
207     */
208    public abstract boolean deleteResource(String name);
209
210    /**
211     * Open an {@link OutputStream} for the specified resource.
212     * @param resourceName to open
213     * @return {@link OutputStream}
214     * @throws IOException on error
215     */
216    protected abstract OutputStream getOutputStream(String resourceName) throws IOException;
217
218    /**
219     * Convert a classname into a resource name.
220     * @param classname to convert
221     * @return String
222     */
223    protected static String getResourceName(final String classname) {
224        return classname.replace('.', '/') + ".class";
225    }
226}