001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.loader.enhancer;
031:
032: import com.caucho.bytecode.ByteCodeParser;
033: import com.caucho.bytecode.JavaClass;
034: import com.caucho.bytecode.JavaField;
035: import com.caucho.bytecode.JavaMethod;
036: import com.caucho.java.WorkDir;
037: import com.caucho.java.gen.JavaClassGenerator;
038: import com.caucho.util.L10N;
039: import com.caucho.vfs.JarPath;
040: import com.caucho.vfs.Path;
041: import com.caucho.vfs.ReadStream;
042: import com.caucho.vfs.Vfs;
043: import com.caucho.vfs.WriteStream;
044:
045: import java.net.URL;
046: import java.util.ArrayList;
047: import java.util.logging.Level;
048: import java.util.logging.Logger;
049:
050: /**
051: * Prepares a class for enhancement.
052: */
053: public class EnhancerPrepare {
054: private static final L10N L = new L10N(EnhancerPrepare.class);
055: private static final Logger log = Logger
056: .getLogger(EnhancerPrepare.class.getName());
057:
058: private static final int ACC_PUBLIC = 0x1;
059: private static final int ACC_PRIVATE = 0x2;
060: private static final int ACC_PROTECTED = 0x4;
061:
062: private JavaClassGenerator _javaGen = new JavaClassGenerator();
063:
064: private ClassLoader _loader;
065:
066: private Path _workPath;
067:
068: private ArrayList<ClassEnhancer> _enhancerList = new ArrayList<ClassEnhancer>();
069:
070: private String _baseSuffix = "";
071: private String _extSuffix = "__ResinExt";
072:
073: private boolean _isParentStarted;
074:
075: /**
076: * Sets the class loader.
077: */
078: public void setClassLoader(ClassLoader loader) {
079: _loader = loader;
080: }
081:
082: /**
083: * Gets the work path.
084: */
085: public Path getWorkPath() {
086: if (_workPath != null)
087: return _workPath;
088: else
089: return WorkDir.getLocalWorkDir(_loader);
090: }
091:
092: /**
093: * Sets the work path.
094: */
095: public void setWorkPath(Path workPath) {
096: _workPath = workPath;
097: }
098:
099: /**
100: * Gets the work path.
101: */
102: public final Path getPreWorkPath() {
103: return getWorkPath().lookup("pre-enhance");
104: }
105:
106: /**
107: * Gets the work path.
108: */
109: public final Path getPostWorkPath() {
110: return getWorkPath().lookup("post-enhance");
111: }
112:
113: /**
114: * Adds a class enhancer.
115: */
116: public void addEnhancer(ClassEnhancer enhancer) {
117: _enhancerList.add(enhancer);
118: }
119:
120: /**
121: * Moves the old class.
122: */
123: public void renameClass(String sourceClass, String targetClass) {
124: Path source = getSource(sourceClass);
125:
126: if (source == null || source.getLastModified() <= 0)
127: return;
128:
129: Path path = getPreWorkPath();
130: Path target = path.lookup(targetClass.replace('.', '/')
131: + ".class");
132:
133: if (target.getLastModified() <= 0
134: || target.getLastModified() < source.getLastModified()) {
135: try {
136: target.remove();
137: } catch (Throwable e) {
138: log.log(Level.WARNING, e.toString(), e);
139: }
140:
141: try {
142: ByteCodeParser parser = new ByteCodeParser();
143: ReadStream is = source.openRead();
144: WriteStream os = null;
145:
146: try {
147: JavaClass cl = parser.parse(is);
148:
149: String cpOldName = sourceClass.replace('.', '/');
150: String cpClassName = targetClass.replace('.', '/');
151:
152: int utf8Index = cl.getConstantPool().addUTF8(
153: cpClassName).getIndex();
154: cl.getConstantPool().getClass(cpOldName)
155: .setNameIndex(utf8Index);
156:
157: cl.setThisClass(cpClassName);
158:
159: // need to set descriptors, too
160:
161: // set private fields to protected
162: ArrayList<JavaField> fields = cl.getFieldList();
163: for (int i = 0; i < fields.size(); i++) {
164: JavaField field = fields.get(i);
165:
166: int accessFlags = field.getAccessFlags();
167:
168: if ((accessFlags & ACC_PRIVATE) != 0) {
169: accessFlags = (accessFlags & ~ACC_PRIVATE)
170: | ACC_PROTECTED;
171: field.setAccessFlags(accessFlags);
172: }
173: }
174:
175: // set private methods to protected
176: ArrayList<JavaMethod> methods = cl.getMethodList();
177: for (int i = 0; i < methods.size(); i++) {
178: JavaMethod method = methods.get(i);
179:
180: int accessFlags = method.getAccessFlags();
181:
182: if ((accessFlags & ACC_PRIVATE) != 0) {
183: accessFlags = (accessFlags & ~ACC_PRIVATE)
184: | ACC_PROTECTED;
185: method.setAccessFlags(accessFlags);
186: }
187: }
188:
189: for (int i = 0; i < _enhancerList.size(); i++) {
190: _enhancerList.get(i).preEnhance(cl);
191: }
192:
193: target.getParent().mkdirs();
194:
195: os = target.openWrite();
196:
197: cl.write(os);
198: } finally {
199: if (is != null)
200: is.close();
201:
202: if (os != null)
203: os.close();
204: }
205: } catch (Exception e) {
206: log.log(Level.WARNING, e.toString(), e);
207:
208: }
209: }
210: }
211:
212: private Path getSource(String className) {
213: ClassLoader loader = _loader;
214: if (loader == null)
215: loader = Thread.currentThread().getContextClassLoader();
216:
217: URL url = loader.getResource(className.replace('.', '/')
218: + ".class");
219:
220: // XXX: workaround for tck
221: String s = url.toString();
222: int index = s.indexOf("jar!/");
223: if (index > 0) {
224: s = s.substring(9, index + 3);
225: Path path = JarPath.create(Vfs.lookup(s));
226: path = path.lookup(className.replace('.', '/') + ".class");
227: return path;
228: }
229:
230: if (url != null)
231: return Vfs.lookup(url.toString());
232: else
233: return null;
234: }
235:
236: /**
237: * Moves the old class.
238: */
239: protected JavaClass renameClass(JavaClass jClass, String targetClass) {
240: String cpOldName = jClass.getThisClass();
241: String cpClassName = targetClass.replace('.', '/');
242:
243: int utf8Index = jClass.getConstantPool().addUTF8(cpClassName)
244: .getIndex();
245: jClass.getConstantPool().getClass(cpOldName).setNameIndex(
246: utf8Index);
247:
248: jClass.setThisClass(cpClassName);
249:
250: // need to set descriptors, too
251:
252: // set private fields to protected
253: ArrayList<JavaField> fields = jClass.getFieldList();
254: for (int i = 0; i < fields.size(); i++) {
255: JavaField field = fields.get(i);
256:
257: int accessFlags = field.getAccessFlags();
258:
259: if ((accessFlags & ACC_PRIVATE) != 0) {
260: accessFlags = (accessFlags & ~ACC_PRIVATE)
261: | ACC_PROTECTED;
262: field.setAccessFlags(accessFlags);
263: }
264: }
265:
266: // set private methods to protected
267: ArrayList<JavaMethod> methods = jClass.getMethodList();
268: for (int i = 0; i < methods.size(); i++) {
269: JavaMethod method = methods.get(i);
270:
271: int accessFlags = method.getAccessFlags();
272:
273: if ((accessFlags & ACC_PRIVATE) != 0) {
274: accessFlags = (accessFlags & ~ACC_PRIVATE)
275: | ACC_PROTECTED;
276: method.setAccessFlags(accessFlags);
277: }
278: }
279:
280: return jClass;
281: }
282: }
|