• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500-V1.0.1.40_1.0.68/ap/gpl/timemachine/db-4.7.25.NC/java/src/com/sleepycat/persist/model/
1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2002,2008 Oracle.  All rights reserved.
5 *
6 * $Id: ClassEnhancer.java,v 1.1 2008/02/07 17:12:28 mark Exp $
7 */
8
9package com.sleepycat.persist.model;
10
11import java.io.File;
12import java.io.FileInputStream;
13import java.io.FileOutputStream;
14import java.io.IOException;
15import java.lang.instrument.ClassFileTransformer;
16import java.lang.instrument.Instrumentation;
17import java.security.ProtectionDomain;
18import java.util.ArrayList;
19import java.util.HashSet;
20import java.util.List;
21import java.util.Set;
22import java.util.StringTokenizer;
23
24import com.sleepycat.asm.ClassReader;
25import com.sleepycat.asm.ClassVisitor;
26import com.sleepycat.asm.ClassWriter;
27
28/**
29 * Enhances the bytecode of persistent classes to provide efficient access to
30 * fields and constructors, and to avoid special security policy settings for
31 * accessing non-public members.  Classes are enhanced if they are annotated
32 * with {@link Entity} or {@link Persistent}.
33 *
34 * <p>{@code ClassEnhancer} objects are thread-safe.  Multiple threads may
35 * safely call the methods of a shared {@code ClassEnhancer} object.</p>
36 *
37 * <p>As described in the {@link <a
38 * href="../package-summary.html#bytecode">package summary</a>}, bytecode
39 * enhancement may be used either at runtime or offline (at build time).</p>
40 *
41 * <p>To use enhancement offline, this class may be used as a {@link #main main
42 * program}.
43 * </p>
44 *
45 * <p>For enhancement at runtime, this class provides the low level support
46 * needed to transform class bytes during class loading.  To configure runtime
47 * enhancement you may use one of the following approaches:</p>
48 * <ol>
49 * <li>For Java 1.5, the {@code je-<version>.jar} file may be used as an instrumentation
50 * agent as follows:
51 * <pre class="code">{@literal java -javaagent:lib/je-<version>.jar=enhance:packageNames ...}</pre>
52 * {@code packageNames} is a comma separated list of packages containing
53 * persistent classes.  Sub-packages of these packages are also searched.  If
54 * {@code packageNames} is omitted then all packages known to the current
55 * classloader are searched.
56 * <p>The "-v" option may be included in the comma separated list to print the
57 * name of each class that is enhanced.</p></li>
58 * <br>
59 * <li>The {@link #enhance} method may be called to implement a class loader
60 * that performs enhancement.  Using this approach, it is the developer's
61 * responsibility to implement and configure the class loader.</li>
62 * </ol>
63 *
64 * @author Mark Hayes
65 */
66public class ClassEnhancer implements ClassFileTransformer {
67
68    private static final String AGENT_PREFIX = "enhance:";
69
70    private Set<String> packagePrefixes;
71    private boolean verbose;
72
73    /**
74     * Enhances classes in the directories specified.  The class files are
75     * replaced when they are enhanced, without changing the file modification
76     * date.  For example:
77     *
78     * <pre class="code">java -cp je-&lt;version&gt;.jar com.sleepycat.persist.model.ClassEnhancer ./classes</pre>
79     *
80     * <p>The "-v" argument may be specified to print the name of each class
81     * file that is enhanced.  The total number of class files enhanced will
82     * always be printed.</p>
83     *
84     * @param args one or more directories containing classes to be enhanced.
85     * Subdirectories of these directories will also be searched.  Optionally,
86     * -v may be included to print the name of every class file enhanced.
87     */
88    public static void main(String[] args) throws Exception {
89        try {
90            boolean verbose = false;
91            List<File> fileList = new ArrayList<File>();
92            for (int i = 0; i < args.length; i += 1) {
93                String arg = args[i];
94                if (arg.startsWith("-")) {
95                    if ("-v".equals(args[i])) {
96                        verbose = true;
97                    } else {
98                        throw new IllegalArgumentException
99                            ("Unknown arg: " + arg);
100                    }
101                } else {
102                    fileList.add(new File(arg));
103                }
104            }
105            ClassEnhancer enhancer = new ClassEnhancer();
106            enhancer.setVerbose(verbose);
107            int nFiles = 0;
108            for (File file : fileList) {
109                nFiles += enhancer.enhanceFile(file);
110            }
111            if (nFiles > 0) {
112                System.out.println("Enhanced: " + nFiles + " files");
113            }
114        } catch (Exception e) {
115            e.printStackTrace();
116            throw e;
117        }
118    }
119
120    /**
121     * Enhances classes as specified by a JVM -javaagent argument.
122     *
123     * @see java.lang.instrument
124     */
125    public static void premain(String args, Instrumentation inst) {
126        if (!args.startsWith(AGENT_PREFIX)) {
127            throw new IllegalArgumentException
128                ("Unknown javaagent args: " + args +
129                 " Args must start with: \"" + AGENT_PREFIX + '"');
130        }
131        args = args.substring(AGENT_PREFIX.length());
132        Set<String> packageNames = null;
133        boolean verbose = false;
134        if (args.length() > 0) {
135            packageNames = new HashSet<String>();
136            StringTokenizer tokens = new StringTokenizer(args, ",");
137            while (tokens.hasMoreTokens()) {
138                String token = tokens.nextToken();
139                if (token.startsWith("-")) {
140                    if (token.equals("-v")) {
141                        verbose = true;
142                    } else {
143                        throw new IllegalArgumentException
144                            ("Unknown javaagent arg: " + token);
145                    }
146                } else {
147                    packageNames.add(token);
148                }
149            }
150        }
151        ClassEnhancer enhancer = new ClassEnhancer(packageNames);
152        enhancer.setVerbose(verbose);
153        inst.addTransformer(enhancer);
154    }
155
156    /**
157     * Creates a class enhancer that searches all packages.
158     */
159    public ClassEnhancer() {
160    }
161
162    /**
163     * Sets verbose mode.
164     *
165     * <p>True may be specified to print the name of each class file that is
166     * enhanced.  This property is false by default.</p>
167     */
168    public void setVerbose(boolean verbose) {
169        this.verbose = verbose;
170    }
171
172    /**
173     * Gets verbose mode.
174     *
175     * @see #setVerbose
176     */
177    public boolean getVerbose() {
178        return verbose;
179    }
180
181    /**
182     * Creates a class enhancer that searches a given set of packages.
183     *
184     * @param packageNames a set of packages to search for persistent
185     * classes.  Sub-packages of these packages are also searched.  If empty or
186     * null, all packages known to the current classloader are searched.
187     */
188    public ClassEnhancer(Set<String> packageNames) {
189        if (packageNames != null) {
190            packagePrefixes = new HashSet<String>();
191            for (String name : packageNames) {
192                packagePrefixes.add(name + '.');
193            }
194        }
195    }
196
197    public byte[] transform(ClassLoader loader,
198                            String className,
199                            Class<?> classBeingRedefined,
200                            ProtectionDomain protectionDomain,
201                            byte[] classfileBuffer) {
202        className = className.replace('/', '.');
203        byte[] bytes = enhance(className, classfileBuffer);
204        if (verbose && bytes != null) {
205            System.out.println("Enhanced: " + className);
206        }
207        return bytes;
208    }
209
210    /**
211     * Enhances the given class bytes if the class is annotated with {@link
212     * Entity} or {@link Persistent}.
213     *
214     * @param className the class name in binary format; for example,
215     * "my.package.MyClass$Name", or null if no filtering by class name
216     * should be performed.
217     *
218     * @param classBytes are the class file bytes to be enhanced.
219     *
220     * @return the enhanced bytes, or null if no enhancement was performed.
221     */
222    public byte[] enhance(String className, byte[] classBytes) {
223        if (className != null && packagePrefixes != null) {
224            for (String prefix : packagePrefixes) {
225                if (className.startsWith(prefix)) {
226                    return enhanceBytes(classBytes);
227                }
228            }
229            return null;
230        } else {
231            return enhanceBytes(classBytes);
232        }
233    }
234
235    int enhanceFile(File file)
236        throws IOException {
237
238        int nFiles = 0;
239        if (file.isDirectory()) {
240            String[] names = file.list();
241            if (names != null) {
242                for (int i = 0; i < names.length; i += 1) {
243                    nFiles += enhanceFile(new File(file, names[i]));
244                }
245            }
246        } else if (file.getName().endsWith(".class")) {
247            byte[] newBytes = enhanceBytes(readFile(file));
248            if (newBytes != null) {
249                long modified = file.lastModified();
250                writeFile(file, newBytes);
251                file.setLastModified(modified);
252                nFiles += 1;
253                if (verbose) {
254                    System.out.println("Enhanced: " + file);
255                }
256            }
257        }
258        return nFiles;
259    }
260
261    private byte[] readFile(File file)
262        throws IOException {
263
264        byte[] bytes = new byte[(int) file.length()];
265        FileInputStream in = new FileInputStream(file);
266        try {
267            in.read(bytes);
268        } finally {
269            in.close();
270        }
271        return bytes;
272    }
273
274    private void writeFile(File file, byte[] bytes)
275        throws IOException {
276
277        FileOutputStream out = new FileOutputStream(file);
278        try {
279            out.write(bytes);
280        } finally {
281            out.close();
282        }
283    }
284
285    private byte[] enhanceBytes(byte[] bytes) {
286
287        /*
288         * The writer is at the end of the visitor chain.  Pass true to
289         * calculate stack size, for safety.
290         */
291        ClassWriter writer = new ClassWriter(true);
292        ClassVisitor visitor = writer;
293
294        /* The enhancer is at the beginning of the visitor chain. */
295        visitor = new BytecodeEnhancer(visitor);
296
297        /* The reader processes the class and invokes the visitors. */
298        ClassReader reader = new ClassReader(bytes);
299        try {
300
301            /*
302             * Pass false for skipDebug since we are rewriting the class and
303             * should include all information.
304             */
305            reader.accept(visitor, false);
306            return writer.toByteArray();
307        } catch (BytecodeEnhancer.NotPersistentException e) {
308            /* The class is not persistent and should not be enhanced. */
309            return null;
310        }
311    }
312}
313