blob: 6b29b145692294076eb35eb49436c1e44ea3b403 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2005 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.j2ee.internal.web.operations;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Iterator;
import java.util.Set;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.codegen.jet.JETEmitter;
import org.eclipse.emf.codegen.jet.JETException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaModelMarker;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jem.util.emf.workbench.ProjectUtilities;
import org.eclipse.jem.util.logger.proxy.Logger;
import org.eclipse.jst.common.internal.annotations.controller.AnnotationsController;
import org.eclipse.jst.common.internal.annotations.controller.AnnotationsControllerManager;
import org.eclipse.jst.common.internal.annotations.controller.AnnotationsControllerManager.Descriptor;
import org.eclipse.jst.j2ee.application.internal.operations.IAnnotationsDataModel;
import org.eclipse.jst.j2ee.internal.common.operations.INewJavaClassDataModelProperties;
import org.eclipse.jst.j2ee.internal.project.WTPJETEmitter;
import org.eclipse.jst.j2ee.internal.web.plugin.WebPlugin;
import org.eclipse.wst.common.componentcore.datamodel.FacetInstallDataModelProvider;
import org.eclipse.wst.common.componentcore.datamodel.FacetProjectCreationDataModelProvider;
import org.eclipse.wst.common.componentcore.datamodel.properties.IFacetDataModelProperties;
import org.eclipse.wst.common.componentcore.datamodel.properties.IFacetProjectCreationDataModelProperties;
import org.eclipse.wst.common.componentcore.datamodel.properties.IFacetProjectCreationDataModelProperties.FacetDataModelMap;
import org.eclipse.wst.common.componentcore.internal.operation.ArtifactEditProviderOperation;
import org.eclipse.wst.common.frameworks.datamodel.DataModelFactory;
import org.eclipse.wst.common.frameworks.datamodel.IDataModel;
import org.eclipse.wst.common.frameworks.internal.enablement.nonui.WFTWrappedException;
import org.eclipse.wst.common.frameworks.internal.plugin.WTPCommonPlugin;
import org.eclipse.wst.common.project.facet.core.IFacetedProject;
import org.eclipse.wst.common.project.facet.core.IProjectFacetVersion;
import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
/**
* The NewServletClassOperation is an IDataModelOperation following the
* IDataModel wizard and operation framework.
*
* @see org.eclipse.wst.common.frameworks.datamodel.IDataModelOperation
* @see org.eclipse.wst.common.frameworks.datamodel.IDataModelProvider
*
* It extends ArtifactEditProviderOperation to provide servlet specific java
* class generation.
* @see org.eclipse.wst.common.componentcore.internal.operation.ArtifactEditProviderOperation
*
* This operation is used by the AddServletOperation to generate either an
* annotated or non annotated java class for an added servlet. It shares the
* NewServletClassDataModelProvider with the AddServletOperation to store the
* appropriate properties required to generate the new servlet.
* @see org.eclipse.jst.j2ee.internal.web.operations.AddServletOperation
* @see org.eclipse.jst.j2ee.internal.web.operations.NewServletClassDataModelProvider
*
* In the annotated case, a WTPJetEmitter servlet template is created and used
* to generate the servlet java class with the embedded annotated tags.
* @see org.eclipse.jst.j2ee.internal.project.WTPJETEmitter
* @see org.eclipse.jst.j2ee.internal.web.operations.CreateServletTemplateModel
*
* In the non annotated case, the same emitter is used to create the class with
* the non annotated servlet template so the annotated tags are omitted.
*
* Subclasses may extend this operation to provide their own specific servlet
* java class generation. The execute method may be extended to do so. Also,
* generateUsingTemplates is exposed.
*
* The use of this class is EXPERIMENTAL and is subject to substantial changes.
*/
public class NewServletClassOperation extends ArtifactEditProviderOperation {
/**
* XDoclet facet constants
*/
private static final String JST_WEB_XDOCLET_VERSION = "1.2.3";
private static final String JST_WEB_XDOCLET = "jst.web.xdoclet";
private static final String WEB_PLUGIN_JAR = "org.eclipse.jst.j2ee.web_1.0.0.jar"; //$NON-NLS-1$
/**
* The extension name for a java class
*/
private static final String DOT_JAVA = ".java"; //$NON-NLS-1$
/**
* platform plugin beginning for URI string
*/
// private static final String PLATFORM_PLUGIN = "platform:/plugin/";
// //$NON-NLS-1$
/**
* variable for the web plugin
*/
protected static final String WEB_PLUGIN = "WEB_PLUGIN"; //$NON-NLS-1$
/**
* folder location of the servlet creation templates diretory
*/
protected static final String TEMPLATE_DIR = "/templates/"; //$NON-NLS-1$
/**
* name of the template emitter to be used to generate the deployment
* descriptor from the tags
*/
protected static final String TEMPLATE_EMITTER = "org.eclipse.jst.j2ee.ejb.annotations.emitter.template"; //$NON-NLS-1$
/**
* id of the builder used to kick off generation of web metadata based on
* parsing of annotations
*/
protected static final String BUILDER_ID = "builderId"; //$NON-NLS-1$
/**
* This is the constructor which should be used when creating a
* NewServletClassOperation. An instance of the NewServletClassDataModel
* should be passed in. This does not accept null parameter. It will not
* return null.
*
* @see ArtifactEditProviderOperation#ArtifactEditProviderOperation(IDataModel)
* @see NewServletClassDataModel
*
* @param dataModel
* @return NewServletClassOperation
*/
public NewServletClassOperation(IDataModel dataModel) {
super(dataModel);
}
/**
* Subclasses may extend this method to add their own actions during
* execution. The implementation of the execute method drives the running of
* the operation. This implemenatation will create the java source folder,
* create the java package, and then if using annotations, will use
* templates to generate an annotated servlet java class, or if it is not
* annotated, the servlet java class file will be created without the
* annotated tags using templates. Optionally, subclasses may extend the
* generateUsingTemplates or createJavaFile method rather than extend the
* execute method. This method will accept a null paramter.
*
* @see org.eclipse.wst.common.frameworks.internal.operation.WTPOperation#execute(org.eclipse.core.runtime.IProgressMonitor)
* @see NewServletClassOperation#generateUsingTemplates(IProgressMonitor,
* IPackageFragment)
*
* @param monitor
* @throws CoreException
* @throws InterruptedException
* @throws InvocationTargetException
*/
public IStatus doExecute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
// Create source folder if it does not exist
createJavaSourceFolder();
// Create java package if it does not exist
IPackageFragment pack = createJavaPackage();
// Generate using templates
try {
generateUsingTemplates(monitor, pack);
} catch (Exception e) {
return WTPCommonPlugin.createErrorStatus(e.toString());
}
return OK_STATUS;
}
/**
* This method will return the java package as specified by the new java
* class data model. If the package does not exist, it will create the
* package. This method should not return null.
*
* @see INewJavaClassDataModelProperties#JAVA_PACKAGE
* @see IPackageFragmentRoot#createPackageFragment(java.lang.String,
* boolean, org.eclipse.core.runtime.IProgressMonitor)
*
* @return IPackageFragment the java package
*/
protected final IPackageFragment createJavaPackage() {
// Retrieve the package name from the java class data model
String packageName = model.getStringProperty(INewJavaClassDataModelProperties.JAVA_PACKAGE);
IPackageFragmentRoot packRoot = (IPackageFragmentRoot) model
.getProperty(INewJavaClassDataModelProperties.JAVA_PACKAGE_FRAGMENT_ROOT);
IPackageFragment pack = packRoot.getPackageFragment(packageName);
// Handle default package
if (pack == null) {
pack = packRoot.getPackageFragment(""); //$NON-NLS-1$
}
// Create the package fragment if it does not exist
if (!pack.exists()) {
String packName = pack.getElementName();
try {
pack = packRoot.createPackageFragment(packName, true, null);
} catch (JavaModelException e) {
Logger.getLogger().log(e);
}
}
// Return the package
return pack;
}
/**
* Subclasses may extend this method to provide their own template based
* creation of an annotated servlet java class file. This implementation
* uses the creation of a CreateServletTemplateModel and the WTPJetEmitter
* to create the java class with the annotated tags. This method accepts
* null for monitor, it does not accept null for fragment. If annotations
* are not being used the tags will be omitted from the class.
*
* @see CreateServletTemplateModel
* @see NewServletClassOperation#generateTemplateSource(CreateServletTemplateModel,
* IProgressMonitor)
*
* @param monitor
* @param fragment
* @throws CoreException
* @throws WFTWrappedException
*/
protected void generateUsingTemplates(IProgressMonitor monitor, IPackageFragment fragment) throws WFTWrappedException, CoreException {
// Create the servlet template model
CreateServletTemplateModel tempModel = createTemplateModel();
IProject project = getTargetProject();
String source;
// Using the WTPJetEmitter, generate the java source based on the
// servlet template model
try {
source = generateTemplateSource(tempModel, monitor);
} catch (Exception e) {
throw new WFTWrappedException(e);
}
if (fragment != null) {
// Create the java file
String javaFileName = tempModel.getServletClassName() + DOT_JAVA;
ICompilationUnit cu = fragment.getCompilationUnit(javaFileName);
// Add the compilation unit to the java file
if (cu == null || !cu.exists())
cu = fragment.createCompilationUnit(javaFileName, source, true, monitor);
IFile aFile = (IFile) cu.getResource();
// Let the annotations controller process the annotated resource
AnnotationsController controller = AnnotationsControllerManager.INSTANCE.getAnnotationsController(project);
if (controller != null)
controller.process(aFile);
// ((J2EEEditModel)model.getEditModel()).getWorkingCopy(cu, true);
// //Track CU.
}
// Add the annotations builder to the java project so metadata can be
// generated.
// TODO for M4 cannot add builder directly here, needs to be set up more
// extensibly
addAnnotationsBuilder(monitor, project);
}
/**
* This method is intended for internal use only. This method will add the
* annotations builder for Xdoclet to the targetted project. This needs to
* be removed from the operation and set up to be more extensible throughout
* the workbench.
*
* @see NewServletClassOperation#generateUsingTemplates(IProgressMonitor,
* IPackageFragment)
*
*
*/
private void addAnnotationsBuilder(IProgressMonitor monitor, IProject project) {
// If an extended annotations processor is added, ignore the default
// xdoclet one
Descriptor descriptor = AnnotationsControllerManager.INSTANCE.getDescriptor(getTargetComponent().getProject());
if (descriptor != null)
return;
try {
// Add the xdoclet facet.
//
installXDocletFacet(monitor, project);
} catch (Exception e) {
// Ignore
}
}
/**
* This method is intended for internal use only. This will add an webdoclet
* facet to the project.
* @throws CoreException
* @throws ExecutionException
*
*/
private void installXDocletFacet(IProgressMonitor monitor, IProject project) throws CoreException, ExecutionException {
IFacetedProject facetedProject = ProjectFacetsManager.create(project);
Set facets = facetedProject.getProjectFacets();
Set fixedFacets = facetedProject.getFixedProjectFacets();
boolean shouldInstallFacet = true;
for (Iterator iter = facets.iterator(); iter.hasNext();) {
IProjectFacetVersion facetVersion = (IProjectFacetVersion) iter.next();
String facetID = facetVersion.getProjectFacet().getId();
if (JST_WEB_XDOCLET.equals(facetID)) {
shouldInstallFacet = false;
}
}
if (!shouldInstallFacet)
return;
IDataModel dm = DataModelFactory.createDataModel(new FacetInstallDataModelProvider());
dm.setProperty(IFacetDataModelProperties.FACET_ID, JST_WEB_XDOCLET);
dm.setProperty(IFacetDataModelProperties.FACET_PROJECT_NAME, project.getName());
dm.setProperty(IFacetDataModelProperties.FACET_VERSION_STR, JST_WEB_XDOCLET_VERSION); //$NON-NLS-1$
IDataModel fdm = DataModelFactory.createDataModel(new FacetProjectCreationDataModelProvider());
fdm.setProperty(IFacetProjectCreationDataModelProperties.FACET_PROJECT_NAME, project.getName());
FacetDataModelMap map = (FacetDataModelMap) fdm.getProperty(IFacetProjectCreationDataModelProperties.FACET_DM_MAP);
map.add(dm);
fdm.getDefaultOperation().execute(monitor, null);
facetedProject.setFixedProjectFacets(fixedFacets);
}
/**
* This method is intended for internal use only. This will use the
* WTPJETEmitter to create an annotated java file based on the passed in
* servlet class template model. This method does not accept null
* parameters. It will not return null. If annotations are not used, it will
* use the non annotated template to omit the annotated tags.
*
* @see NewServletClassOperation#generateUsingTemplates(IProgressMonitor,
* IPackageFragment)
* @see JETEmitter#generate(org.eclipse.core.runtime.IProgressMonitor,
* java.lang.Object[])
* @see CreateServletTemplateModel
*
* @param tempModel
* @param monitor
* @return String the source for the java file
* @throws JETException
*/
private String generateTemplateSource(CreateServletTemplateModel tempModel, IProgressMonitor monitor) throws JETException {
URL templateURL = null;
// If annotated, use annotated template
if (model.getBooleanProperty(IAnnotationsDataModel.USE_ANNOTATIONS))
templateURL = WebPlugin.getDefault().find(
new Path(TEMPLATE_DIR + getDataModel().getStringProperty(INewServletClassDataModelProperties.TEMPLATE_FILE)));
// Otherwise use non annotated template
else
templateURL = WebPlugin.getDefault().find(
new Path(TEMPLATE_DIR
+ getDataModel().getStringProperty(INewServletClassDataModelProperties.NON_ANNOTATED_TEMPLATE_FILE)));
cleanUpOldEmitterProject();
WTPJETEmitter emitter = new WTPJETEmitter(templateURL.toString(), this.getClass().getClassLoader());
emitter.setIntelligentLinkingEnabled(true);
emitter.addVariable(WEB_PLUGIN, WebPlugin.PLUGIN_ID);
return emitter.generate(monitor, new Object[] { tempModel });
}
private void cleanUpOldEmitterProject() {
IProject project = ProjectUtilities.getProject(WTPJETEmitter.PROJECT_NAME);
if (project == null || !project.exists())
return;
try {
IMarker[] markers = project.findMarkers(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
for (int i = 0, l = markers.length; i < l; i++) {
if (((Integer) markers[i].getAttribute(IMarker.SEVERITY)).intValue() == IMarker.SEVERITY_ERROR) {
project.delete(true, new NullProgressMonitor());
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* This method is intended for internal use only. This method will create an
* instance of the CreateServletTemplate model to be used in conjunction
* with the WTPJETEmitter. This method will not return null.
*
* @see CreateServletTemplateModel
* @see NewServletClassOperation#generateUsingTemplates(IProgressMonitor,
* IPackageFragment)
*
* @return CreateServletTemplateModel
*/
private CreateServletTemplateModel createTemplateModel() {
// Create the CreateServletTemplateModel instance with the new servlet
// class data model
CreateServletTemplateModel templateModel = new CreateServletTemplateModel(model);
return templateModel;
}
/**
* This method will return the java source folder as specified in the java
* class data model. It will create the java source folder if it does not
* exist. This method may return null.
*
* @see INewJavaClassDataModelProperties#SOURCE_FOLDER
* @see IFolder#create(boolean, boolean,
* org.eclipse.core.runtime.IProgressMonitor)
*
* @return IFolder the java source folder
*/
protected final IFolder createJavaSourceFolder() {
// Get the source folder name from the data model
String folderFullPath = model.getStringProperty(INewJavaClassDataModelProperties.SOURCE_FOLDER);
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IFolder folder = root.getFolder(new Path(folderFullPath));
// If folder does not exist, create the folder with the specified path
if (!folder.exists()) {
try {
folder.create(true, true, null);
} catch (CoreException e) {
Logger.getLogger().log(e);
}
}
// Return the source folder
return folder;
}
}