| /********************************************************************** |
| * Copyright (c) 2007, 2008 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Igor Fedorenko & Fabrizio Giustina - Initial API and implementation |
| **********************************************************************/ |
| package org.eclipse.jst.server.tomcat.core.internal; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.jst.server.tomcat.core.internal.wst.IModuleVisitor; |
| import org.eclipse.jst.server.tomcat.core.internal.xml.Factory; |
| import org.eclipse.jst.server.tomcat.core.internal.xml.server40.Context; |
| import org.eclipse.jst.server.tomcat.core.internal.xml.server40.Loader; |
| import org.eclipse.jst.server.tomcat.core.internal.xml.server40.ServerInstance; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.wst.common.componentcore.resources.IVirtualComponent; |
| import org.eclipse.wst.common.componentcore.resources.IVirtualFile; |
| import org.eclipse.wst.server.core.IModule; |
| import org.eclipse.wst.server.core.ServerUtil; |
| |
| /** |
| * Handles "publishing" for servers that can load classes and resources directly |
| * from the workspace. Instead of creating and deploying jars to the webapp this |
| * simply update the virtual classpath in the context xml file. |
| */ |
| public class TomcatPublishModuleVisitor implements IModuleVisitor { |
| |
| /** |
| * Server base path (Catalina base). |
| */ |
| protected final IPath baseDir; |
| |
| /** |
| * Server instance in which to modify the context |
| */ |
| protected final ServerInstance serverInstance; |
| |
| /** |
| * Catalina.properties loader to add global classpath entries |
| */ |
| protected final String sharedLoader; |
| |
| /** |
| * Classpath entries added by ear configurations. |
| */ |
| protected final List earCommonResources = new ArrayList(); |
| |
| /** |
| * List of classpath elements that will be used by the custom tomcat loader. |
| * This set should include any class dir from referenced project. |
| */ |
| protected Set virtualClassClasspathElements = new LinkedHashSet(); |
| protected Set virtualJarClasspathElements = new LinkedHashSet(); |
| |
| /** |
| * Instantiate a new TomcatPublishModuleVisitor |
| * |
| * @param catalinaBase catalina base path |
| */ |
| TomcatPublishModuleVisitor(IPath catalinaBase, ServerInstance serverInstance, String sharedLoader) { |
| this.baseDir = catalinaBase; |
| this.serverInstance = serverInstance; |
| this.sharedLoader = sharedLoader; |
| } |
| |
| /** |
| * @see IModuleVisitor#visitWebComponent(IVirtualComponent) |
| */ |
| public void visitWebComponent(IVirtualComponent component) |
| throws CoreException { |
| // nothing to do, everything is done in endVisitWebComponent |
| } |
| |
| /** |
| * @see IModuleVisitor#visitArchiveComponent(IPath, IPath) |
| */ |
| public void visitArchiveComponent(IPath runtimePath, IPath workspacePath) { |
| addVirtualJarResource(runtimePath, workspacePath); |
| } |
| |
| /** |
| * @see IModuleVisitor#visitDependentComponent(IPath, IPath) |
| */ |
| public void visitDependentComponent(IPath runtimePath, IPath workspacePath) { |
| addVirtualJarResource(runtimePath, workspacePath); |
| } |
| |
| /** |
| * @see IModuleVisitor#visitWebResource(IPath, IPath) |
| */ |
| public void visitWebResource(IPath runtimePath, IPath workspacePath) { |
| addVirtualClassResource(runtimePath, workspacePath); |
| } |
| |
| /** |
| * @see IModuleVisitor#visitEarResource(IPath, IPath) |
| */ |
| public void visitEarResource(IPath runtimePath, IPath workspacePath) { |
| earCommonResources.add(workspacePath.toOSString()); |
| } |
| |
| /** |
| * @see IModuleVisitor#endVisitEarComponent(IVirtualComponent) |
| */ |
| public void endVisitEarComponent(IVirtualComponent component) |
| throws CoreException { |
| if (earCommonResources.size() > 0) { |
| try { |
| CatalinaPropertiesUtil.addGlobalClasspath(baseDir.append( |
| "conf/catalina.properties").toFile(), sharedLoader, |
| (String[]) earCommonResources.toArray(new String[earCommonResources.size()])); |
| } catch (IOException e) { |
| Trace.trace(Trace.WARNING, "Unable to add ear path entries to catalina.properties", e); |
| } finally { |
| earCommonResources.clear(); |
| } |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void endVisitWebComponent(IVirtualComponent component) |
| throws CoreException { |
| |
| // track context changes, don't rewrite if not needed |
| boolean dirty = false; |
| |
| IModule module = ServerUtil.getModule(component.getProject()); |
| |
| // we need this for the user-specified context path |
| Context context = findContext(module); |
| if (context == null) { |
| String name = module != null ? module.getName() : component.getName(); |
| Trace.trace(Trace.SEVERE, "Could not find context for module " + name); |
| throw new CoreException(new Status(IStatus.ERROR, TomcatPlugin.PLUGIN_ID, 0, |
| NLS.bind(Messages.errorPublishContextNotFound, name), null)); |
| } |
| |
| String contextName = null; |
| boolean reloadable = true; |
| |
| contextName = context.getPath(); |
| reloadable = Boolean.valueOf(context.getReloadable()).booleanValue(); |
| |
| // now strip initial / |
| if (contextName.startsWith("/")) { |
| contextName = contextName.substring(1); |
| } |
| |
| // root context is deployed with the "ROOT" name in tomcat |
| if ("".equals(contextName)) { |
| contextName = "ROOT"; |
| } |
| |
| // handle project context.xml |
| Context projectContext = getProjectContextXml(component); |
| |
| if (projectContext != null) { |
| // copy configuration to server context |
| projectContext.copyChildrenTo(context); |
| |
| Map attrs = projectContext.getAttributes(); |
| Iterator iter = attrs.keySet().iterator(); |
| while (iter.hasNext()) { |
| String name = (String) iter.next(); |
| if (!name.equalsIgnoreCase("path") |
| && !name.equalsIgnoreCase("docBase") |
| && !name.equalsIgnoreCase("source")) { |
| String value = (String) attrs.get(name); |
| String actualValue = context.getAttributeValue(name); |
| if (!value.equals(actualValue)) { |
| context.setAttributeValue(name, value); |
| dirty = true; |
| } |
| } |
| } |
| } |
| |
| // handle changes in docBase |
| String docBase = component.getRootFolder().getUnderlyingFolder() |
| .getLocation().toOSString(); |
| if (!docBase.equals(context.getDocBase())) { |
| dirty = true; |
| context.setDocBase(docBase); |
| } |
| |
| // handle changes in reloadable flag |
| if (reloadable != (Boolean.valueOf((context.getReloadable())) |
| .booleanValue())) { |
| dirty = true; |
| context.setReloadable(Boolean.toString(reloadable)); |
| } |
| |
| String path = (contextName.equals("ROOT") ? "" : "/" + contextName); |
| // handle changes in the path |
| // PATH is required for tomcat 5.0, but ignored in tomcat 5.5 |
| if (!path.equals(context.getPath())) { |
| dirty = true; |
| context.setPath(path); |
| } |
| |
| context.getResources().setClassName( |
| "org.eclipse.jst.server.tomcat.loader.WtpDirContext"); |
| |
| Loader loader = context.getLoader(); |
| |
| loader.setClassName("org.eclipse.jst.server.tomcat.loader.WtpWebappLoader"); |
| |
| // required for tomcat 5.5.20 due to the change in |
| // http://issues.apache.org/bugzilla/show_bug.cgi?id=39704 |
| loader.setUseSystemClassLoaderAsParent(Boolean.FALSE.toString()); |
| |
| // write down the virtual classPath |
| StringBuffer buffer = new StringBuffer(); |
| for (Iterator iterator = virtualClassClasspathElements.iterator(); |
| iterator.hasNext();) { |
| buffer.append(iterator.next()); |
| if (iterator.hasNext()) { |
| buffer.append(";"); |
| } |
| } |
| if (buffer.length() > 0 && virtualJarClasspathElements.size() > 0) { |
| buffer.append(";"); |
| } |
| for (Iterator iterator = virtualJarClasspathElements.iterator(); |
| iterator.hasNext();) { |
| buffer.append(iterator.next()); |
| if (iterator.hasNext()) { |
| buffer.append(";"); |
| } |
| } |
| virtualClassClasspathElements.clear(); |
| virtualJarClasspathElements.clear(); |
| |
| String vcp = buffer.toString(); |
| |
| String oldVcp = loader.getVirtualClasspath(); |
| |
| if (!vcp.equals(oldVcp)) { |
| // save only if needed |
| dirty = true; |
| loader.setVirtualClasspath(vcp); |
| context.getResources().setVirtualClasspath(vcp); |
| } |
| |
| if (dirty) { |
| //TODO If writing to separate context XML files, save "dirty" status for later use |
| } |
| } |
| |
| private void addVirtualClassResource(IPath runtimePath, IPath workspacePath) { |
| virtualClassClasspathElements.add(workspacePath.toOSString()); |
| } |
| |
| private void addVirtualJarResource(IPath runtimePath, IPath workspacePath) { |
| virtualJarClasspathElements.add(workspacePath.toOSString()); |
| } |
| |
| /** |
| * Load a META-INF/context.xml file from project, if available |
| * |
| * @param component web component containing the context.xml |
| * @return context element containing the context.xml |
| * @throws CoreException |
| */ |
| protected Context getProjectContextXml(IVirtualComponent component) |
| throws CoreException { |
| |
| // load or create module's context.xml document |
| IVirtualFile contextFile = (IVirtualFile) component.getRootFolder() |
| .findMember("META-INF/context.xml"); |
| |
| Context contextElement = null; |
| |
| if (contextFile != null && contextFile.exists()) { |
| |
| Factory factory = new Factory(); |
| factory.setPackageName("org.eclipse.jst.server.tomcat.core.internal.xml.server40"); |
| |
| InputStream fis = null; |
| try { |
| fis = contextFile.getUnderlyingFile().getContents(); |
| contextElement = (Context) factory.loadDocument(fis); |
| } catch (Exception e) { |
| Trace.trace(Trace.SEVERE, "Exception reading " + contextFile, e); |
| } finally { |
| try { |
| fis.close(); |
| } catch (IOException e) { |
| // ignore |
| } |
| } |
| } |
| return contextElement; |
| } |
| |
| /** |
| * Returns the given module from the config. |
| * |
| * @param module a web module |
| * @return a web module |
| */ |
| protected Context findContext(IModule module) { |
| if (module == null) { |
| return null; |
| } |
| |
| String source = module.getId(); |
| |
| Context [] contexts = serverInstance.getContexts(); |
| for (int i = 0; i < contexts.length; i++) { |
| if (source.equals(contexts[i].getSource())) |
| return contexts[i]; |
| } |
| return null; |
| } |
| } |