001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.visualweb.complib;
043:
044: import java.beans.BeanInfo;
045: import java.beans.IntrospectionException;
046: import java.beans.Introspector;
047: import java.io.BufferedInputStream;
048: import java.io.File;
049: import java.io.FileInputStream;
050: import java.io.IOException;
051: import java.io.InputStream;
052: import java.net.MalformedURLException;
053: import java.net.URL;
054: import java.net.URLClassLoader;
055: import java.util.ArrayList;
056: import java.util.Iterator;
057: import java.util.List;
058: import java.util.Properties;
059: import java.util.jar.Manifest;
060:
061: import org.netbeans.modules.visualweb.classloaderprovider.CommonClassloaderProvider;
062: import org.netbeans.modules.visualweb.complib.api.ComplibException;
063: import org.openide.util.Lookup;
064: import org.openide.util.Lookup.Result;
065:
066: /**
067: * Represents an extension component library provided by a third party. There are two main use cases
068: * for this class:
069: * <nl>
070: * <li>Creating from a package file</li>
071: * <li>Creating from an existing expanded directory</li>
072: * </nl>
073: *
074: * <p>
075: * The first case is used during initial component import in the UI which must be fast. At this time
076: * only a limited amount of information from the complib manifest such as the component library
077: * title can be accessed via a few methods. Calling other methods requires that the complib package
078: * first be expanded. Once expanded, any method can be called.
079: * </p>
080: *
081: * <p>
082: * The second case is used when a library is already installed and is persisted on disk in expanded
083: * form.
084: * </p>
085: *
086: * @author Edwin Goei
087: */
088: public class ExtensionComplib extends Complib {
089:
090: /**
091: * ClassLoader used to load l10n resources from a *.complib file
092: *
093: * @author Edwin Goei
094: */
095: private static class ResourceClassLoader extends URLClassLoader {
096:
097: public ResourceClassLoader(URL packageFileUrl) {
098: super (new URL[0], ResourceClassLoader.class
099: .getClassLoader());
100: addURL(packageFileUrl);
101: }
102: }
103:
104: /**
105: * ClassLoader used to load classes from an expanded component library
106: *
107: * @author Edwin Goei
108: */
109: private static class LibraryClassLoader extends URLClassLoader {
110: private static final ClassLoader parentClassLoader;
111: static {
112: /*
113: * Use Java EE 5 common class loader as a superset. It contains classes from older EE
114: * API versions, the Creator design-time API, and also classes that should not
115: * technically be available but this is a convenient implementation. Code copied from
116: * insync ModelSet().
117: */
118: CommonClassloaderProvider commonClassloaderProvider = null;
119:
120: Properties capabilities = new Properties();
121: capabilities.put(CommonClassloaderProvider.J2EE_PLATFORM,
122: CommonClassloaderProvider.JAVA_EE_5);
123: Result result = Lookup.getDefault()
124: .lookup(
125: new Lookup.Template(
126: CommonClassloaderProvider.class));
127: for (Iterator iterator = result.allInstances().iterator(); iterator
128: .hasNext();) {
129: CommonClassloaderProvider aCommonClassloaderProvider = (CommonClassloaderProvider) iterator
130: .next();
131: if (aCommonClassloaderProvider
132: .isCapableOf(capabilities)) {
133: commonClassloaderProvider = aCommonClassloaderProvider;
134: break;
135: }
136: }
137:
138: if (commonClassloaderProvider == null) {
139: throw new RuntimeException(
140: "No Common Classloader Provider found."); // TODO I18N
141: }
142:
143: parentClassLoader = commonClassloaderProvider
144: .getClassLoader();
145: }
146:
147: public LibraryClassLoader() {
148: super (new URL[0], parentClassLoader);
149: }
150:
151: public void appendToClassPath(List<File> path) {
152: for (File file : path) {
153: try {
154: super .addURL(file.toURI().toURL());
155: } catch (MalformedURLException e) {
156: // Should not normally happen, output warning
157: IdeUtil.logWarning(e);
158: }
159: }
160: }
161: }
162:
163: private static final String MANIFEST_FILE = "META-INF/MANIFEST.MF"; // NOI18N
164:
165: /** Component library package file which may always be null */
166: private File packageFile;
167:
168: /**
169: * Component library directory is absolute, may temporarily be null. Non-null value iff package
170: * is expanded.
171: */
172: private File absoluteLibDir;
173:
174: /**
175: * ClassLoader used to load resources referenced in package file metadata such as the manifest
176: * and initial-palette config file. These resources can be accessed before or after a package is
177: * expanded.
178: */
179: private ResourceClassLoader resourceClassLoader;
180:
181: /**
182: * ClassLoader for loading classes in this complib. This can only be used after a package is
183: * expanded.
184: */
185: private LibraryClassLoader libClassLoader;
186:
187: /**
188: * @param absoluteLibDir
189: * lib directory root
190: * @throws ComplibException
191: * @throws IOException
192: */
193: ExtensionComplib(File absoluteLibDir) throws ComplibException,
194: IOException {
195: // The complib has already been expanded
196: assert absoluteLibDir != null;
197: File file = new File(absoluteLibDir, MANIFEST_FILE);
198: InputStream in = new BufferedInputStream(new FileInputStream(
199: file));
200: Manifest manifest = new Manifest(in);
201: URL absLibDirUrl = absoluteLibDir.toURI().toURL();
202: this .resourceClassLoader = new ResourceClassLoader(absLibDirUrl);
203: ComplibManifest compLibManifest = ComplibManifest.getInstance(
204: manifest, resourceClassLoader);
205: initCompLibManifest(compLibManifest);
206: in.close();
207:
208: this .absoluteLibDir = absoluteLibDir;
209:
210: // Convert String paths in ComplibManifest into File paths
211: initPaths();
212: }
213:
214: protected List<File> convertConfigPathToFileList(List<String> path)
215: throws ComplibException {
216: ArrayList<File> retVal = new ArrayList<File>(path.size());
217: for (String pathElm : path) {
218: File file = new File(absoluteLibDir, pathElm);
219: if (!file.canRead()) {
220: throw new ComplibException("Unable to read '" // NOI18N
221: + file + "'"); // NOI18N
222: }
223: retVal.add(file);
224: }
225: return retVal;
226: }
227:
228: protected File[] convertConfigPathToFileArray(List<String> path)
229: throws ComplibException {
230: File[] retVal = new File[path.size()];
231: for (int i = 0; i < path.size(); i++) {
232: String pathElm = (String) path.get(i);
233: File file = new File(absoluteLibDir, pathElm);
234: if (!file.canRead()) {
235: throw new ComplibException("Unable to read '" // NOI18N
236: + file + "'"); // NOI18N
237: }
238: retVal[i] = file;
239: }
240: return retVal;
241: }
242:
243: /**
244: * @return basename of this library's package file or null if none
245: */
246: String getPackageFileBaseName() {
247: return (packageFile == null) ? null : packageFile.getName();
248: }
249:
250: /**
251: * Returns the base name of the root of this component library. The library must already be
252: * expanded.
253: *
254: * @return Returns the base name of this component library
255: */
256: public String getDirectoryBaseName() {
257: return getDirectory().getName();
258: }
259:
260: /**
261: * Returns the absolute root of this component library. The library must already be expanded.
262: *
263: * @return Returns the absolute root of this component library, never null
264: */
265: public File getDirectory() {
266: return absoluteLibDir;
267: }
268:
269: public ClassLoader getClassLoader() {
270: if (libClassLoader == null) {
271: libClassLoader = new LibraryClassLoader();
272: libClassLoader.appendToClassPath(getRuntimePath());
273: libClassLoader.appendToClassPath(getDesignTimePath());
274: libClassLoader.appendToClassPath(getHelpPath());
275: }
276: return libClassLoader;
277: }
278:
279: @Override
280: BeanInfo getBeanInfo(String className)
281: throws ClassNotFoundException, IntrospectionException {
282: // 6393979 Simulate an appropriate context ClassLoader.
283: // Temporarily set the context ClassLoader and restore it later.
284: // TODO Possibly remove this code when class loading is fixed in Mako.
285: ClassLoader origContextLoader = null;
286: try {
287: origContextLoader = Thread.currentThread()
288: .getContextClassLoader();
289: Thread.currentThread().setContextClassLoader(
290: getClassLoader());
291:
292: // Do the real work here
293: Class beanClass = Class.forName(className, true,
294: getClassLoader());
295: return Introspector.getBeanInfo(beanClass);
296: } finally {
297: Thread.currentThread().setContextClassLoader(
298: origContextLoader);
299: }
300: }
301:
302: @Override
303: public String toString() {
304: return getIdentifier().toString();
305: }
306: }
|