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-2006 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.tomcat5;
043:
044: import java.io.File;
045: import java.io.IOException;
046: import java.lang.reflect.InvocationTargetException;
047: import java.lang.reflect.Method;
048: import java.net.MalformedURLException;
049: import java.net.URL;
050: import java.net.URLClassLoader;
051: import java.util.WeakHashMap;
052: import java.util.logging.Level;
053: import java.util.logging.Logger;
054: import javax.enterprise.deploy.shared.factories.DeploymentFactoryManager;
055: import javax.enterprise.deploy.spi.DeploymentManager;
056: import javax.enterprise.deploy.spi.exceptions.DeploymentManagerCreationException;
057: import javax.enterprise.deploy.spi.factories.DeploymentFactory;
058: import org.netbeans.modules.j2ee.deployment.devmodules.api.Deployment;
059: import org.netbeans.modules.j2ee.deployment.devmodules.spi.InstanceListener;
060: import org.netbeans.modules.j2ee.deployment.plugins.api.InstanceProperties;
061: import org.netbeans.modules.tomcat5.TomcatManager.TomcatVersion;
062: import org.netbeans.modules.tomcat5.util.TomcatInstallUtil;
063: import org.netbeans.modules.tomcat5.util.TomcatProperties;
064: import org.openide.filesystems.FileObject;
065: import org.openide.filesystems.FileUtil;
066: import org.openide.filesystems.Repository;
067: import org.openide.util.Exceptions;
068: import org.openide.util.Lookup;
069: import org.openide.util.NbBundle;
070: import org.openide.util.NbPreferences;
071:
072: /**
073: * Factory capable to create DeploymentManager that can deploy to Tomcat 5 and 6.
074: *
075: * Tomcat URI has following format:
076: * <PRE><CODE>tomcat[55|60]:[home=<home_path>:[base=<base_path>:]]<manager_app_url></CODE></PRE>
077: * for example
078: * <PRE><CODE>tomcat:http://localhost:8080/manager/</CODE></PRE>
079: * where paths values will be used as CATALINA_HOME and CATALINA_BASE properties and manager_app_url
080: * denotes URL of manager application configured on this server and has to start with <CODE>http:</CODE>.
081: * @author Radim Kubacki
082: */
083: public final class TomcatFactory implements DeploymentFactory {
084:
085: public static final String SERVER_ID_50 = "Tomcat"; // NOI18N
086: public static final String SERVER_ID_55 = "Tomcat55"; // NOI18N
087: public static final String SERVER_ID_60 = "Tomcat60"; // NOI18N
088:
089: public static final String TOMCAT_URI_PREFIX_50 = "tomcat:"; // NOI18N
090: public static final String TOMCAT_URI_PREFIX_55 = "tomcat55:"; // NOI18N
091: public static final String TOMCAT_URI_PREFIX_60 = "tomcat60:"; // NOI18N
092:
093: public static final String TOMCAT_URI_HOME_PREFIX = "home="; // NOI18N
094: public static final String TOMCAT_URI_BASE_PREFIX = ":base="; // NOI18N
095:
096: private static final String DISCONNECTED_URI_50 = "tomcat:jakarta-tomcat-5.0.x"; // NOI18N
097: private static final String DISCONNECTED_URI_55 = "tomcat55:jakarta-tomcat-5.5.x"; // NOI18N
098: private static final String DISCONNECTED_URI_60 = "tomcat60:apache-tomcat-6.0.x"; // NOI18N
099:
100: private static TomcatFactory instance;
101: private static TomcatFactory instance55;
102: private static TomcatFactory instance60;
103:
104: private static final WeakHashMap managerCache = new WeakHashMap();
105:
106: private static Logger err = Logger
107: .getLogger("org.netbeans.modules.tomcat5"); // NOI18N
108:
109: private final String tomcatUriPrefix;
110: private final String disconnectedUri;
111: private final TomcatVersion version;
112:
113: /**
114: * System property pointing to the 'bundled' Tomcat catalina home directory.
115: * This is used for example by installer to register the default Tomcat server
116: * instance.
117: */
118: private static final String PROP_CATALINA_HOME = "org.netbeans.modules.tomcat.autoregister.catalinaHome"; // NOI18N
119: private static final String PROP_TOKEN = "org.netbeans.modules.tomcat.autoregister.token"; // NOI18N
120: private static final String PROP_REMOVED_INSTANCE_TOKEN = "removed_instance_token"; // NOI18N
121:
122: static {
123: autoregisterTomcatInstance();
124: }
125:
126: private TomcatFactory(TomcatVersion version) {
127: this .version = version;
128: switch (version) {
129: case TOMCAT_50:
130: tomcatUriPrefix = TOMCAT_URI_PREFIX_50;
131: disconnectedUri = DISCONNECTED_URI_50;
132: break;
133: case TOMCAT_55:
134: tomcatUriPrefix = TOMCAT_URI_PREFIX_55;
135: disconnectedUri = DISCONNECTED_URI_55;
136: break;
137: case TOMCAT_60:
138: default:
139: tomcatUriPrefix = TOMCAT_URI_PREFIX_60;
140: disconnectedUri = DISCONNECTED_URI_60;
141: break;
142: }
143: }
144:
145: /**
146: * Factory method to create DeploymentFactory for Tomcat 5.0.x
147: */
148: public static synchronized TomcatFactory create50() {
149: if (instance == null) {
150: if (err.isLoggable(Level.FINE))
151: err.log(Level.FINE, "Creating TomcatFactory"); // NOI18N
152: instance = new TomcatFactory(TomcatVersion.TOMCAT_50);
153: DeploymentFactoryManager.getInstance()
154: .registerDeploymentFactory(instance);
155: }
156: return instance;
157: }
158:
159: /**
160: * Factory method to create DeploymentFactory for Tomcat 5.5.x
161: */
162: public static synchronized TomcatFactory create55() {
163: if (instance55 == null) {
164: if (err.isLoggable(Level.FINE))
165: err.log(Level.FINE, "Creating TomcatFactory"); // NOI18N
166: instance55 = new TomcatFactory(TomcatVersion.TOMCAT_55);
167: DeploymentFactoryManager.getInstance()
168: .registerDeploymentFactory(instance55);
169: }
170: return instance55;
171: }
172:
173: /**
174: * Factory method to create DeploymentFactory for Tomcat 6.0.x
175: */
176: public static synchronized TomcatFactory create60() {
177: if (instance60 == null) {
178: if (err.isLoggable(Level.FINE))
179: err.log(Level.FINE, "Creating TomcatFactory"); // NOI18N
180: instance60 = new TomcatFactory(TomcatVersion.TOMCAT_60);
181: DeploymentFactoryManager.getInstance()
182: .registerDeploymentFactory(instance60);
183: }
184: return instance60;
185: }
186:
187: /** Factory method to create DeploymentManager.
188: * @param uri URL of configured manager application.
189: * @param uname user with granted manager role
190: * @param passwd user's password
191: * @throws DeploymentManagerCreationException
192: * @return {@link TomcatManager}
193: */
194: public synchronized DeploymentManager getDeploymentManager(
195: String uri, String uname, String passwd)
196: throws DeploymentManagerCreationException {
197: if (!handlesURI(uri)) {
198: throw new DeploymentManagerCreationException("Invalid URI:"
199: + uri); // NOI18N
200: }
201: // Lets reuse the same instance of TomcatManager for each server instance
202: // during the IDE session, j2eeserver does not ensure this. Without it,
203: // however, we could not rely on keeping data in the member variables.
204: InstanceProperties ip = InstanceProperties
205: .getInstanceProperties(uri);
206: if (ip == null) {
207: // null ip either means that the instance is not registered, or that this is the disconnected URL
208: if (!disconnectedUri.equals(uri)) {
209: throw new DeploymentManagerCreationException(
210: "Tomcat instance: " + uri
211: + " is not registered in the IDE."); // NOI18N
212: }
213: }
214: TomcatManager tm = (TomcatManager) managerCache.get(ip);
215: if (tm == null) {
216: try {
217: tm = new TomcatManager(true, uri
218: .substring(tomcatUriPrefix.length()), version);
219: managerCache.put(ip, tm);
220: } catch (IllegalArgumentException iae) {
221: Throwable t = new DeploymentManagerCreationException(
222: "Cannot create deployment manager for Tomcat instance: "
223: + uri + "."); // NOI18N
224: throw (DeploymentManagerCreationException) (t
225: .initCause(iae));
226: }
227: }
228: return tm;
229: }
230:
231: public DeploymentManager getDisconnectedDeploymentManager(String uri)
232: throws DeploymentManagerCreationException {
233: // no need to distinguish beetween the connected and disconnected DM for Tomcat
234: return getDeploymentManager(uri, null, null);
235: }
236:
237: public String getDisplayName() {
238: switch (version) {
239: case TOMCAT_50:
240: return NbBundle.getMessage(TomcatFactory.class,
241: "LBL_TomcatFactory");
242: case TOMCAT_55:
243: return NbBundle.getMessage(TomcatFactory.class,
244: "LBL_TomcatFactory55");
245: case TOMCAT_60:
246: default:
247: return NbBundle.getMessage(TomcatFactory.class,
248: "LBL_TomcatFactory60");
249: }
250: }
251:
252: public String getProductVersion() {
253: return NbBundle.getMessage(TomcatFactory.class,
254: "LBL_TomcatFactoryVersion");
255: }
256:
257: /**
258: * @param str
259: * @return <CODE>true</CODE> for URIs beggining with <CODE>tomcat[55|60]:</CODE> prefix
260: */
261: public boolean handlesURI(String str) {
262: return str != null && str.startsWith(tomcatUriPrefix);
263: }
264:
265: /**
266: * Retrieve the tomcat version e.g. '6.0.10'
267: *
268: * @throws IllegalStateException if the version information cannot be retrieved
269: */
270: private static String getTomcatVersion(File catalinaHome)
271: throws IllegalStateException {
272: File catalinaJar = new File(catalinaHome, "lib/catalina.jar"); // NOI18N
273: if (!catalinaJar.exists()) {
274: catalinaJar = new File(catalinaHome,
275: "server/lib/catalina.jar"); // NOI18N
276: }
277: try {
278: URLClassLoader loader = new URLClassLoader(
279: new URL[] { catalinaJar.toURL() });
280: Class serverInfo = loader
281: .loadClass("org.apache.catalina.util.ServerInfo"); // NOI18N
282: Method method = serverInfo.getMethod("getServerInfo",
283: new Class[] {}); // NOI18N
284: String version = (String) method.invoke(serverInfo,
285: new Object[] {});
286: int idx = version.indexOf('/');
287: if (idx > 0) {
288: return version.substring(idx + 1);
289: }
290: throw new IllegalStateException(
291: "Cannot identify the version of the server."); // NOI18N
292: } catch (MalformedURLException e) {
293: throw new IllegalStateException(e);
294: } catch (ClassNotFoundException e) {
295: throw new IllegalStateException(e);
296: } catch (NoSuchMethodException e) {
297: throw new IllegalStateException(e);
298: } catch (InvocationTargetException e) {
299: throw new IllegalStateException(e);
300: } catch (IllegalAccessException e) {
301: throw new IllegalStateException(e);
302: }
303: }
304:
305: /**
306: * Auto-registers Tomcat server instance defined by the {@link #PROP_CATALINA_HOME}
307: * property.
308: */
309: private static void autoregisterTomcatInstance() {
310:
311: Repository repository = (Repository) Lookup.getDefault()
312: .lookup(Repository.class);
313: FileObject serverInstanceDir = repository
314: .getDefaultFileSystem().findResource(
315: "/J2EE/InstalledServers"); // NOI18N
316:
317: if (serverInstanceDir == null) {
318: err
319: .log(
320: Level.INFO,
321: "Cannot register the default Tomcat server. The //J2EE//InstalledServers folder cannot be found."); // NOI18N
322: return;
323: }
324:
325: String catalinaHomeValue = System
326: .getProperty(PROP_CATALINA_HOME);
327: final String token = System.getProperty(PROP_TOKEN);
328:
329: // look up the auto-registgered server instance FO
330: FileObject autoregInstanceFO = null;
331: for (FileObject fo : serverInstanceDir.getChildren()) {
332: if (Boolean
333: .parseBoolean((String) fo
334: .getAttribute(TomcatProperties.PROP_AUTOREGISTERED))) {
335: autoregInstanceFO = fo;
336: break;
337: }
338: }
339:
340: // if the system properties are no longer set, unregister if needed
341: if (catalinaHomeValue == null || token == null) {
342: if (autoregInstanceFO != null) {
343: try {
344: autoregInstanceFO.delete();
345: } catch (IOException e) {
346: err
347: .log(
348: Level.INFO,
349: "The server "
350: + autoregInstanceFO
351: .getAttribute(InstanceProperties.URL_ATTR)
352: + " cannot be uregistered."); // NOI18N
353: Exceptions.printStackTrace(e);
354: }
355: }
356: return;
357: }
358:
359: String removedToken = NbPreferences.forModule(
360: TomcatFactory.class).get(PROP_REMOVED_INSTANCE_TOKEN,
361: null);
362: if (token.equals(removedToken)) {
363: // this server instance has been already removed, do not proceed
364: return;
365: }
366:
367: File catalinaHome = new File(catalinaHomeValue);
368: if (!catalinaHome.exists()) {
369: err.log(Level.INFO,
370: "Cannot register the default Tomcat server. "
371: + "The Catalina Home directory "
372: + catalinaHomeValue
373: + // NOI18N
374: " passed through the " + PROP_CATALINA_HOME
375: + " property does not exist."); // NOI18N
376: return;
377: }
378:
379: String version;
380: try {
381: version = getTomcatVersion(catalinaHome);
382: } catch (IllegalStateException e) {
383: err
384: .log(
385: Level.INFO,
386: "Cannot register the default Tomcat server. Cannot recognize the Tomcat version."); // NOI18N
387: err.log(Level.INFO, null, e);
388: return;
389: }
390:
391: // build URL
392: StringBuilder urlTmp;
393: if (version.startsWith("5.0.")) { // NOI18N
394: urlTmp = new StringBuilder(TOMCAT_URI_PREFIX_50);
395: } else if (version.startsWith("5.5.")) { // NOI18N
396: urlTmp = new StringBuilder(TOMCAT_URI_PREFIX_55);
397: } else if (version.startsWith("6.")) { // NOI18N
398: urlTmp = new StringBuilder(TOMCAT_URI_PREFIX_60);
399: } else {
400: err.log(Level.INFO,
401: "Cannot register the default Tomcat server. "
402: + " The version " + version
403: + " is not supported."); // NOI18N
404: return;
405: }
406: urlTmp.append(TOMCAT_URI_HOME_PREFIX);
407: urlTmp.append(catalinaHomeValue);
408: urlTmp.append(TOMCAT_URI_BASE_PREFIX);
409: urlTmp.append("apache-tomcat-"); // NOI18N
410: urlTmp.append(version);
411: urlTmp.append("_base"); // NOI18N
412:
413: final String url = urlTmp.toString();
414:
415: // listen to server instance removals
416: Deployment.getDefault().addInstanceListener(
417: new InstanceListener() {
418: public void changeDefaultInstance(
419: String oldServerInstanceID,
420: String newServerInstanceID) {
421: }
422:
423: public void instanceAdded(String serverInstanceID) {
424: }
425:
426: public void instanceRemoved(String serverInstanceID) {
427: if (url.equals(serverInstanceID)) {
428: // the auto-registered instance was removed, remember it
429: NbPreferences
430: .forModule(TomcatFactory.class)
431: .put(PROP_REMOVED_INSTANCE_TOKEN,
432: token);
433: }
434: }
435: });
436:
437: // make sure the server is not registered yet
438: for (FileObject fo : serverInstanceDir.getChildren()) {
439: if (url
440: .equals(fo
441: .getAttribute(InstanceProperties.URL_ATTR))) {
442: // the server is already registered, do nothing
443: return;
444: }
445: }
446:
447: // if the auto-registered instance has changed, removed the old one
448: if (autoregInstanceFO != null
449: && !url.equals(autoregInstanceFO
450: .getAttribute(InstanceProperties.URL_ATTR))) {
451: try {
452: autoregInstanceFO.delete();
453: } catch (IOException e) {
454: err
455: .log(
456: Level.INFO,
457: "The server "
458: + autoregInstanceFO
459: .getAttribute(InstanceProperties.URL_ATTR)
460: + " cannot be uregistered."); // NOI18N
461: Exceptions.printStackTrace(e);
462: }
463: }
464:
465: String displayName = generateUniqueDisplayName(
466: serverInstanceDir, version);
467: registerServerInstanceFO(serverInstanceDir, url, displayName);
468: }
469:
470: /**
471: * Generates a unique display name for the specified version of Tomcat
472: *
473: * @param serverInstanceDir /J2EE/InstalledServers folder
474: * @param version Tomcat version
475: *
476: * @return a unique display name for the specified version of Tomcat
477: */
478: private static String generateUniqueDisplayName(
479: FileObject serverInstanceDir, String version) {
480: // find a unique display name
481: String displayName = NbBundle.getMessage(TomcatFactory.class,
482: "LBL_ApacheTomcat", version);
483: boolean unique = true;
484: int i = 1;
485: while (true) {
486: for (FileObject fo : serverInstanceDir.getChildren()) {
487: if (displayName
488: .equals(fo
489: .getAttribute(InstanceProperties.DISPLAY_NAME_ATTR))) {
490: // there is already some server of the same name
491: unique = false;
492: break;
493: }
494: }
495: if (unique) {
496: break;
497: }
498: displayName = NbBundle.getMessage(TomcatFactory.class,
499: "LBL_ApacheTomcatAlt", version, i++);
500: unique = true;
501: }
502: ;
503: return displayName;
504: }
505:
506: /**
507: * Registers the server instance file object and set the default properties.
508: *
509: * @param serverInstanceDir /J2EE/InstalledServers folder
510: * @param url server instance url/ID
511: * @param displayName display name
512: */
513: private static void registerServerInstanceFO(
514: FileObject serverInstanceDir, String url, String displayName) {
515: String name = FileUtil.findFreeFileName(serverInstanceDir,
516: "tomcat_autoregistered_instance", null); // NOI18N
517: FileObject instanceFO;
518: try {
519: instanceFO = serverInstanceDir.createData(name);
520: instanceFO.setAttribute(InstanceProperties.URL_ATTR, url);
521: instanceFO.setAttribute(InstanceProperties.USERNAME_ATTR,
522: "ide"); // NOI18N
523: String password = TomcatInstallUtil.generatePassword(8);
524: instanceFO.setAttribute(InstanceProperties.PASSWORD_ATTR,
525: password);
526: instanceFO.setAttribute(
527: InstanceProperties.DISPLAY_NAME_ATTR, displayName);
528: instanceFO.setAttribute(
529: InstanceProperties.HTTP_PORT_NUMBER, "8084"); // NOI18N
530: instanceFO.setAttribute(TomcatProperties.PROP_SHUTDOWN,
531: "8025"); // NOI18N
532: instanceFO.setAttribute(TomcatProperties.PROP_MONITOR,
533: "true"); // NOI18N
534: instanceFO.setAttribute(TomcatManager.PROP_BUNDLED_TOMCAT,
535: "true"); // NOI18N
536: instanceFO.setAttribute(
537: TomcatProperties.PROP_AUTOREGISTERED, "true"); // NOI18N
538: } catch (IOException e) {
539: err.log(Level.INFO,
540: "Cannot register the default Tomcat server."); // NOI18N
541: err.log(Level.INFO, null, e);
542: }
543: }
544: }
|