/*******************************************************************************
* Copyright 2011 Google Inc. All Rights Reserved.
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.google.gdt.eclipse.designer.actions.deploy;
import com.google.common.collect.Lists;
import com.google.gdt.eclipse.designer.actions.AbstractModuleAction;
import com.google.gdt.eclipse.designer.common.Constants;
import com.google.gdt.eclipse.designer.launch.GwtLaunchConfigurationDelegate;
import com.google.gdt.eclipse.designer.model.module.ModuleElement;
import com.google.gdt.eclipse.designer.model.module.ServletElement;
import com.google.gdt.eclipse.designer.model.web.WebUtils;
import com.google.gdt.eclipse.designer.util.ModuleVisitor;
import com.google.gdt.eclipse.designer.util.Utils;
import com.google.gdt.eclipse.designer.wizards.Activator;
import org.eclipse.wb.internal.core.DesignerPlugin;
import org.eclipse.wb.internal.core.utils.IOUtils2;
import org.eclipse.wb.internal.core.utils.Version;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.window.Window;
import org.apache.commons.lang.StringUtils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
/**
* Action for deployment selected GWT module into application server.
*
* @author scheglov_ke
* @coverage gwt.deploy
*/
public class DeployModuleAction extends AbstractModuleAction {
////////////////////////////////////////////////////////////////////////////
//
// Action implementation
//
////////////////////////////////////////////////////////////////////////////
@Override
protected void selectedModuleChanged(IAction action) {
if (m_selectedModule != null) {
action.setEnabled(true);
action.setToolTipText("Deploy module '"
+ m_selectedModule.getId()
+ "' on application server");
} else {
action.setEnabled(false);
action.setToolTipText("No selected GWT module to deploy");
}
}
@Override
protected void runWithSelectedModule() throws Exception {
final DeployDialog deployDialog =
new DeployDialog(DesignerPlugin.getShell(), Activator.getDefault(), m_selectedModule);
if (deployDialog.open() != Window.OK) {
return;
}
//
IRunnableWithProgress runnableWithProgress = new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException,
InterruptedException {
try {
monitor.beginTask("Deployment of module '" + m_selectedModule.getId() + "'", 1);
// create script
{
monitor.worked(1);
createBuildScript(deployDialog);
}
// execute script
{
IProject project = m_selectedModule.getProject();
IFolder targetFolder = m_selectedModule.getModuleFolder();
File buildFile = targetFolder.getFile("build.xml").getLocation().toFile();
AntHelper antHelper = new AntHelper(buildFile, project.getLocation().toFile());
antHelper.execute(monitor);
}
} catch (Throwable e) {
throw new InvocationTargetException(e);
}
}
};
ProgressMonitorDialog progressMonitorDialog =
new ProgressMonitorDialog(DesignerPlugin.getShell());
progressMonitorDialog.run(true, false, runnableWithProgress);
}
////////////////////////////////////////////////////////////////////////////
//
// ANT Script
//
////////////////////////////////////////////////////////////////////////////
/**
* Creates build.xml file in same folder as module.
*/
private void createBuildScript(DeployDialog deployDialog) throws Exception {
String moduleId = m_selectedModule.getId();
//
IProject project = m_selectedModule.getProject();
IFolder targetFolder = m_selectedModule.getModuleFolder();
// prepare script
String script = getScriptTemplate();
// apply template values
script = StringUtils.replace(script, "##GWT_HOME##", Utils.getGWTLocation(project));
script = StringUtils.replace(script, "##PROJECT_ROOT##", project.getLocation().toOSString());
script = StringUtils.replace(script, "##MODULE_ID##", moduleId);
script = StringUtils.replace(script, "##WAR_NAME##", deployDialog.getWarName());
script = StringUtils.replace(script, "##DEPLOY_DIR##", deployDialog.getServerPath());
script = StringUtils.replace(script, "##COMPILER_STYLE##", deployDialog.getCompilerStyle());
script =
StringUtils.replace(script, "##COMPILER_MAX_MEMORY##", deployDialog.getCompilerMaxMemory());
// set class path elements
{
String classpathElements = "";
String[] classpath = getGWTProjectClasspath(project);
for (int i = 0; i < classpath.length; i++) {
String element = classpath[i].replace('\\', '/');
classpathElements += "\t\t\t\t<pathelement location=\"" + element + "\" />";
if (i != classpath.length - 1) {
classpathElements += "\n";
}
}
script = StringUtils.replace(script, "##CLASS_PATH_ELEMENTS##", classpathElements);
}
// set servlets
{
final StringBuffer buffer = new StringBuffer();
ModuleVisitor.accept(m_selectedModule, new ModuleVisitor() {
@Override
public void endVisitModule(ModuleElement module) {
List<ServletElement> servletElements = module.getServletElements();
for (ServletElement servletElement : servletElements) {
String servletName = servletElement.getClassName();
{
buffer.append("\t<servlet>\n");
buffer.append("\t\t<servlet-name>" + servletName + "</servlet-name>\n");
buffer.append("\t\t<servlet-class>"
+ servletElement.getClassName()
+ "</servlet-class>\n");
buffer.append("\t</servlet>\n");
}
{
buffer.append("\t<servlet-mapping>\n");
buffer.append("\t\t<servlet-name>" + servletName + "</servlet-name>\n");
buffer.append("\t\t<url-pattern>" + servletElement.getPath() + "</url-pattern>\n");
buffer.append("\t</servlet-mapping>\n");
}
}
}
});
// apply sevlets
script = StringUtils.replace(script, "##SERVLETS##", buffer.toString());
}
// create/copy jar's
{
String targetModulePath = WebUtils.getWebFolderName(project);
String jarsScript = prepareJars(project, targetModulePath, true);
jarsScript = StringUtils.chomp(jarsScript, "\n");
script = StringUtils.replace(script, "<!--##PROJECT_AND_REQUIRED_JARS##-->", jarsScript);
}
// create build.xml
IFile buildFile = targetFolder.getFile("build.xml");
ByteArrayInputStream source = new ByteArrayInputStream(script.getBytes());
if (buildFile.exists()) {
buildFile.setContents(source, true, true, null);
} else {
buildFile.create(source, true, null);
}
}
private String getScriptTemplate() throws Exception {
IProject project = m_selectedModule.getProject();
String buildVersion = getBuildVersion(project).getStringMajorMinor();
String scriptName = "build-" + buildVersion + ".xml";
// try to find script near to module
{
IFile scriptFile = m_selectedModule.getModuleFolder().getFile(new Path(scriptName));
if (scriptFile.exists()) {
return IOUtils2.readString(scriptFile);
}
}
// OK, load default script
return IOUtils2.readString(DeployModuleAction.class.getResourceAsStream(scriptName));
}
/**
* @return the version of build file, not exactly same as version of GWT.
*/
private static Version getBuildVersion(IProject project) {
Version version = Utils.getVersion(project);
if (version.isHigherOrSame(Utils.GWT_2_0)) {
version = Utils.GWT_2_0;
}
return version;
}
////////////////////////////////////////////////////////////////////////////
//
// Script: jars
//
////////////////////////////////////////////////////////////////////////////
/**
* Returns ANT code for creating jar's for given project itself, coping its jar's from classpath
* and calls itself for required projects.
*/
private static String prepareJars(IProject project,
String targetModulePath,
boolean addRuntimeJars) throws Exception {
String script = "";
//
IJavaProject javaProject = JavaCore.create(project);
IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots();
// add <jar> task for creating jar from project source and output folders
{
List<String> sourceLocations = Lists.newArrayList();
List<String> binaryLocations = Lists.newArrayList();
for (IPackageFragmentRoot packageFragmentRoot : roots) {
if (packageFragmentRoot.getKind() == IPackageFragmentRoot.K_SOURCE) {
// add source location
sourceLocations.add(packageFragmentRoot.getResource().getLocation().toPortableString());
// add output location
{
// prepare output location
IPath location;
{
IClasspathEntry cpEntry = packageFragmentRoot.getRawClasspathEntry();
location = cpEntry.getOutputLocation();
if (location == null) {
location = javaProject.getOutputLocation();
}
}
// add absolute location
{
// remove first segment (project)
location = location.removeFirstSegments(1);
// prepare absolute location
IPath absoluteLocation = project.getLocation().append(location);
binaryLocations.add(absoluteLocation.toPortableString());
}
}
}
}
//
script += "\t\t<!--=== " + project.getName() + " ===-->\n";
script +=
"\t\t<jar destfile='"
+ targetModulePath
+ "/WEB-INF/lib/"
+ project.getName()
+ ".jar'>\n";
script += prepareFileSets(sourceLocations, "**");
script += prepareFileSets(binaryLocations, "**/*.class");
script += "\t\t</jar>\n";
}
// add <copy> task for coping required runtime jar's
if (addRuntimeJars) {
String jars = "";
IRuntimeClasspathEntry[] classpathEntries =
JavaRuntime.computeUnresolvedRuntimeClasspath(javaProject);
for (int entryIndex = 0; entryIndex < classpathEntries.length; entryIndex++) {
IRuntimeClasspathEntry entry = classpathEntries[entryIndex];
IRuntimeClasspathEntry[] resolvedEntries =
JavaRuntime.resolveRuntimeClasspathEntry(entry, javaProject);
for (int resolvedEntryIndex = 0; resolvedEntryIndex < resolvedEntries.length; resolvedEntryIndex++) {
IRuntimeClasspathEntry resolvedEntry = resolvedEntries[resolvedEntryIndex];
if (resolvedEntry.getClasspathProperty() == IRuntimeClasspathEntry.USER_CLASSES
&& resolvedEntry.getType() == IRuntimeClasspathEntry.ARCHIVE) {
String location = resolvedEntry.getLocation();
// exclude gwt-user.jar, it is in classpath, but it has Servlet class, so can not be in application
if (location.endsWith("gwt-user.jar")) {
continue;
}
// add jar file in fileset
jars += "\t\t\t<fileset file=\"" + location + "\"/>\n";
}
}
}
//
if (jars.length() != 0) {
script += "\t\t<copy todir='" + targetModulePath + "/WEB-INF/lib'>\n";
script += jars;
script += "\t\t</copy>\n";
}
}
// add required projects
{
IProject[] referencedProjects = project.getReferencedProjects();
for (int i = 0; i < referencedProjects.length; i++) {
IProject referencedProject = referencedProjects[i];
script += prepareJars(referencedProject, targetModulePath, false);
}
}
//
return script;
}
/**
* Prepares source of <fileset>/<include> for given list of directories and inclusion pattern.
*/
private static String prepareFileSets(List<String> dirs, String includePattern) {
String result = "";
for (String dir : dirs) {
result += "\t\t\t<fileset dir=\"" + dir + "\">\n";
result += "\t\t\t\t<include name=\"" + includePattern + "\"/>\n";
result += "\t\t\t</fileset>\n";
}
return result;
}
////////////////////////////////////////////////////////////////////////////
//
// Classpath
//
////////////////////////////////////////////////////////////////////////////
/**
* @return GWT runtime classpath for given {@link IProject}.
*/
public static String[] getGWTProjectClasspath(IProject project) throws CoreException {
// create new launch configuration just as container for parameters, we are not going to save it
ILaunchConfigurationWorkingCopy launchConfiguration;
{
ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
ILaunchConfigurationType launchConfigurationType =
launchManager.getLaunchConfigurationType(Constants.LAUNCH_TYPE_ID_SHELL);
launchConfiguration = launchConfigurationType.newInstance(null, "__GWTDesigner_tmp");
}
// set project name
launchConfiguration.setAttribute(
IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME,
project.getName());
// create delegate just for asking classpath
GwtLaunchConfigurationDelegate delegate = new GwtLaunchConfigurationDelegate();
return delegate.getClasspathAll(launchConfiguration);
}
}