001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.netui.util.internal;
020:
021: import java.net.URL;
022: import java.net.MalformedURLException;
023: import java.io.File;
024: import java.io.FileInputStream;
025: import java.io.ByteArrayOutputStream;
026: import java.io.FileNotFoundException;
027: import java.io.BufferedInputStream;
028: import java.io.IOException;
029:
030: import org.apache.beehive.netui.util.internal.concurrent.InternalConcurrentHashMap;
031: import java.util.Map;
032: import java.util.Iterator;
033: import java.security.SecureClassLoader;
034:
035: import org.apache.beehive.netui.util.logging.Logger;
036:
037: /**
038: * ClassLoader that takes precendence over its parent for loading classes, and which is aware of timestamps on its
039: * binaries (and thus knows when it needs to be bounced).
040: */
041: public class BouncyClassLoader extends SecureClassLoader {
042: private static final Logger _log = Logger
043: .getInstance(BouncyClassLoader.class);
044:
045: private InternalConcurrentHashMap/*< File, Long >*/_timestamps = new InternalConcurrentHashMap/*< File, Long >*/();
046: private File[] _classDirs;
047:
048: public BouncyClassLoader(File[] classDirs, ClassLoader parent) {
049: super (parent);
050: _classDirs = classDirs;
051: }
052:
053: public Class loadClass(String name) throws ClassNotFoundException {
054: Class ret = findLoadedClass(name);
055: if (ret != null)
056: return ret;
057:
058: String baseName = File.separatorChar
059: + name.replace('.', File.separatorChar)
060: .concat(".class");
061:
062: for (int i = 0; i < _classDirs.length; ++i) {
063: File file = new File(_classDirs[i].getPath() + baseName);
064:
065: if (file.exists()) {
066: _timestamps.put(file, new Long(file.lastModified()));
067: byte[] bytes = getBytes(file);
068:
069: if (_log.isDebugEnabled()) {
070: _log.debug("Loading class " + name + " from file "
071: + file);
072: }
073: if (bytes != null)
074: return super .defineClass(name, bytes, 0,
075: bytes.length);
076: }
077: }
078:
079: return super .loadClass(name);
080: }
081:
082: public URL getResource(String name) {
083: for (int i = 0; i < _classDirs.length; ++i) {
084: File file = new File(_classDirs[i].getPath(), name);
085:
086: if (file.exists()) {
087: _timestamps.put(file, new Long(file.lastModified()));
088:
089: try {
090: if (_log.isDebugEnabled()) {
091: _log.debug("Loading resource " + name
092: + " from file " + file);
093: }
094: return file.toURL();
095: } catch (MalformedURLException e) {
096: // Shouldn't happen if the file exists.
097: e.printStackTrace();
098: assert false : "malformed URL for file "
099: + file.getPath();
100: }
101: }
102: }
103:
104: return super .getResource(name);
105: }
106:
107: private byte[] getBytes(File file) {
108: try {
109: BufferedInputStream in = new BufferedInputStream(
110: new FileInputStream(file));
111:
112: try {
113: ByteArrayOutputStream out = new ByteArrayOutputStream();
114: int c;
115:
116: while ((c = in.read()) != -1) {
117: out.write(c);
118: }
119:
120: return out.toByteArray();
121: } finally {
122: in.close();
123: }
124: } catch (FileNotFoundException e) {
125: _log.error("Could not read class file " + file, e);
126: } catch (IOException e) {
127: _log.error("Error while reading class file " + file, e);
128: }
129:
130: return null;
131: }
132:
133: public boolean isStale() {
134: for (Iterator i = _timestamps.entrySet().iterator(); i
135: .hasNext();) {
136: Map.Entry entry = (Map.Entry) i.next();
137: if (((File) entry.getKey()).lastModified() > ((Long) entry
138: .getValue()).longValue()) {
139: if (_log.isDebugEnabled()) {
140: _log.debug("Detected modified class/resource in "
141: + entry.getKey());
142: }
143: return true;
144: }
145: }
146:
147: return false;
148: }
149: }
|