/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.runtime.ui.actions;
import static org.teiid.designer.metamodels.relational.extension.RestModelExtensionConstants.NAMESPACE_PROVIDER;
import static org.teiid.designer.runtime.ui.DqpUiConstants.UTIL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IPath;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.teiid.core.designer.ModelerCoreException;
import org.teiid.core.designer.util.CoreStringUtil;
import org.teiid.core.designer.util.I18nUtil;
import org.teiid.core.designer.util.StringConstants;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.metamodel.aspect.sql.SqlAspectHelper;
import org.teiid.designer.core.workspace.ModelObjectAnnotationHelper;
import org.teiid.designer.core.workspace.ModelResource;
import org.teiid.designer.extension.ExtensionPlugin;
import org.teiid.designer.extension.definition.ModelObjectExtensionAssistant;
import org.teiid.designer.metamodels.relational.Procedure;
import org.teiid.designer.metamodels.relational.extension.RestModelExtensionConstants;
import org.teiid.designer.metamodels.relational.impl.ProcedureImpl;
import org.teiid.designer.runtime.DqpPlugin;
import org.teiid.designer.runtime.ui.DqpUiConstants;
import org.teiid.designer.runtime.ui.DqpUiPlugin;
import org.teiid.designer.runtime.ui.wizards.webservices.RestWarDeploymentInfoDialog;
import org.teiid.designer.runtime.ui.wizards.webservices.util.RestProcedure;
import org.teiid.designer.runtime.ui.wizards.webservices.util.WarArchiveUtil;
import org.teiid.designer.ui.actions.ISelectionAction;
import org.teiid.designer.ui.common.eventsupport.SelectionUtilities;
import org.teiid.designer.ui.viewsupport.ModelIdentifier;
import org.teiid.designer.vdb.Vdb;
import org.teiid.designer.vdb.VdbModelEntry;
import org.teiid.designer.vdb.XmiVdb;
/**
* @since 8.0
*/
public class GenerateRestWarAction extends Action implements ISelectionListener, Comparable, ISelectionAction {
static final String I18N_PREFIX = I18nUtil.getPropertyPrefix(GenerateRestWarAction.class);
private static final String VDB_EXTENSION = "vdb"; //$NON-NLS-1$
private static final ModelObjectAnnotationHelper ANNOTATION_HELPER = new ModelObjectAnnotationHelper();
private IFile selectedVDB;
// Map of models containing restful procedures
private Map<String, List<RestProcedure>> restfulProcedureMap = new HashMap<String, List<RestProcedure>>();
private Properties designerProperties;
/**
*
*/
public GenerateRestWarAction() {
this.setText(UTIL.getString(I18N_PREFIX + "text")); //$NON-NLS-1$
this.setToolTipText(UTIL.getString(I18N_PREFIX + "tooltip")); //$NON-NLS-1$
this.setImageDescriptor(DqpUiPlugin.getDefault().getImageDescriptor(DqpUiConstants.Images.CREATE_WAR));
setDisabledImageDescriptor(DqpUiPlugin.getDefault().getImageDescriptor(DqpUiConstants.Images.CREATE_WAR));
setEnabled(false);
}
/**
* @param properties Designer properties for WAR generation
*/
public void setDesingerProperties(Properties properties) {
this.designerProperties = properties;
}
@Override
public int compareTo( Object o ) {
if (o instanceof String) {
return getText().compareTo((String)o);
}
if (o instanceof Action) {
return getText().compareTo(((Action)o).getText());
}
return 0;
}
/**
* @param selection
* @return whether selection is applicable
*/
@Override
public boolean isApplicable( ISelection selection ) {
boolean result = false;
if (!SelectionUtilities.isMultiSelection(selection)) {
Object obj = SelectionUtilities.getSelectedObject(selection);
if (obj instanceof IFile) {
String extension = ((IFile)obj).getFileExtension();
if (extension != null && extension.equals("vdb")) { //$NON-NLS-1$
result = true;
}
}
}
return result;
}
/**
* @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
*/
@Override
public void run() {
final IWorkbenchWindow window = DqpUiPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
boolean cont = true;
if (compiler == null) {
cont = MessageDialog.openConfirm(window.getShell(), UTIL.getString(I18N_PREFIX + "javaWarningTitle"), //$NON-NLS-1$
UTIL.getString(I18N_PREFIX + "invalidJDKMessage")); //$NON-NLS-1$
}
if (!cont) {
notifyResult(false);
return;
}
RestWarDeploymentInfoDialog dialog = null;
dialog = new RestWarDeploymentInfoDialog(window.getShell(), this.selectedVDB, this.restfulProcedureMap, null, this.designerProperties);
int rc = dialog.open();
// Retrieve the file name for the confirmation dialog
String warFileName = dialog.getWarFileName();
final String successMessage = UTIL.getString(I18N_PREFIX + "warFileCreated", warFileName); //$NON-NLS-1$
boolean wasSuccessful = (rc == Window.OK);
if (wasSuccessful) {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
MessageDialog.openInformation(window.getShell(), UTIL.getString(I18N_PREFIX + "creationCompleteTitle"),//$NON-NLS-1$
successMessage);
}
});
} else {
if (rc != Window.CANCEL) {
MessageDialog.openError(window.getShell(), UTIL.getString(I18N_PREFIX + "creationFailedTitle"),//$NON-NLS-1$
dialog.getMessage());
}
}
notifyResult(rc == Window.OK);
}
/**
* @param result
* @param obj
*/
private static boolean isVdb(Object obj) {
if (obj == null)
return false;
if (! (obj instanceof IFile))
return false;
return VDB_EXTENSION.equals(((IFile) obj).getFileExtension());
}
/**
* @param eObjectList
* @return list of rest procedures
*/
private static List<RestProcedure> findRestProcedures(ModelResource modelResource) throws Exception {
List<RestProcedure> restfulProcedureArray = new ArrayList<RestProcedure>();
Collection<EObject> eObjectList = modelResource.getEObjects();
for (EObject eObject : eObjectList) {
if (SqlAspectHelper.isProcedure(eObject)) {
IPath path = ModelerCore.getModelEditor().getModelRelativePathIncludingModel(eObject);
final StringBuffer sb = new StringBuffer();
final String[] segments = path.segments();
for (int i = 0; i < segments.length; i++) {
if (i != 0) {
sb.append('.');
}
final String segment = segments[i];
sb.append(segment);
}
String fullName = sb.toString();
String name = ((ProcedureImpl)eObject).getName();
createRestProcedureCollection((Procedure)eObject, name, fullName, restfulProcedureArray);
}
}
return restfulProcedureArray;
}
/**
* @param selection
* @return if selection was set
*/
public boolean setSelection(ISelection selection) {
if (SelectionUtilities.isMultiSelection(selection))
return false;
Object obj = SelectionUtilities.getSelectedObject(selection);
// If a VDB is selected and it contains a web service model then
// enable
if (! (obj instanceof IFile))
return false;
if (! isVdb(obj))
return false;
this.selectedVDB = (IFile)obj;
boolean result = false;
try {
Vdb vdb = new XmiVdb(this.selectedVDB);
Set<VdbModelEntry> modelEntrySet = vdb.getModelEntries();
restfulProcedureMap.clear();
for (VdbModelEntry vdbModelEntry : modelEntrySet) {
final ModelResource modelResource = ModelerCore.getModelWorkspace().findModelResource(vdbModelEntry.getPath());
if (! ModelIdentifier.isVirtualModelType(modelResource))
continue;
List<RestProcedure> restfulProcedureArray = findRestProcedures(modelResource);
if (restfulProcedureArray.size() > 0) {
String modelName = vdbModelEntry.getName();
restfulProcedureMap.put(modelName, restfulProcedureArray);
result = true;
}
}
} catch (Exception ex) {
DqpPlugin.Util.log(ex);
return false;
}
return result;
}
@Override
public void selectionChanged( IWorkbenchPart part,
ISelection selection ) {
boolean enable = setSelection(selection);
setEnabled(enable);
}
private static String getHeaders( Procedure procedure ) {
Object headers = null;
try {
// try new way first
ModelObjectExtensionAssistant assistant = (ModelObjectExtensionAssistant)ExtensionPlugin.getInstance()
.getRegistry()
.getModelExtensionAssistant(NAMESPACE_PROVIDER.getNamespacePrefix());
headers = assistant.getPropertyValue(procedure, RestModelExtensionConstants.PropertyIds.HEADERS);
if (headers==null || CoreStringUtil.isEmpty((String)headers)) {
headers = ANNOTATION_HELPER.getPropertyValueAnyCase(procedure,
ModelObjectAnnotationHelper.EXTENDED_PROPERTY_NAMESPACE
+ "headers"); //$NON-NLS-1$
}
} catch (Exception e) {
UTIL.log(e);
}
return headers==null?StringConstants.EMPTY_STRING:(String)headers;
}
private static String getCharset( Procedure procedure ) {
String charset = null;
try {
// try new way first
ModelObjectExtensionAssistant assistant = (ModelObjectExtensionAssistant)ExtensionPlugin.getInstance()
.getRegistry()
.getModelExtensionAssistant(NAMESPACE_PROVIDER.getNamespacePrefix());
charset = assistant.getPropertyValue(procedure, RestModelExtensionConstants.PropertyIds.CHARSET);
if (CoreStringUtil.isEmpty(charset)) {
charset = (String)ANNOTATION_HELPER.getPropertyValueAnyCase(procedure,
ModelObjectAnnotationHelper.EXTENDED_PROPERTY_NAMESPACE
+ "CHARSET"); //$NON-NLS-1$
}
} catch (Exception e) {
UTIL.log(e);
}
return charset;
}
/**
* @param eObject
* @throws ModelerCoreException
*/
private static void createRestProcedureCollection( Procedure procedure,
String name,
String fullName,
List restfulProcedureArray ) {
String restMethod = WarArchiveUtil.getRestMethod(procedure);
LinkedList<String> queryParameterList = new LinkedList<String>();
LinkedList<String> headerParameterList = new LinkedList<String>();
if (restMethod != null) {
String uri = WarArchiveUtil.getUri(procedure);
String charSet = getCharset(procedure);
if (charSet==null){
charSet=Charset.defaultCharset().name();
}
// the procedure is not eligible for REST exposure without a URI defined
if (uri != null) {
boolean hasReturn = false;
int parameterCount = procedure.getParameters().size();
int uriParameterCount = 0;
RestProcedure restProcedure = new RestProcedure();
// Get all child EObjects for this procedure
EList<EObject> contents = procedure.eContents();
for (EObject eobject : contents) {
// If this is a result set, set hasReturn true and we will
// add the produces annotation to the RestProcedure instance
if (SqlAspectHelper.isProcedureResultSet(eobject)) {
hasReturn = true;
continue;
}
}
//Check for HTTP Header parameters
String headers = getHeaders(procedure);
if (headers.length()>0){
String[] headerParameterArray = headers.split(";"); //$NON-NLS-1$
for (String param : headerParameterArray) {
headerParameterList.addLast(param);
}
}
//Check for URI parameters
String uriString = uri;
for (int i = 0; i < uriString.length(); i++) {
String character = uriString.substring(i, i + 1);
if (character.equals("{")) { //$NON-NLS-1$
uriParameterCount++;
}
}
//Check for query parameters
if (uriString.indexOf("&")>-1){ //$NON-NLS-1$
String[] queryParameterArray = uriString.split("&"); //$NON-NLS-1$
int i = 0;
for (String param : queryParameterArray) {
i++;
if (i==1) {
uri= param; //Set the first token as our URI and continue
continue;
}
queryParameterList.addLast(param);
}
}
restProcedure.setDescription(WarArchiveUtil.getRestDescription(procedure));
restProcedure.setCharSet(charSet);
restProcedure.setRestMethod(restMethod);
restProcedure.setUri(uri);
restProcedure.setProcedureName(name);
restProcedure.setFullyQualifiedProcedureName(fullName);
restProcedure.setQueryParameterList(queryParameterList);
restProcedure.setHeaderParameterList(headerParameterList);
// Create JSON version
RestProcedure jsonRestProcedure = new RestProcedure();
jsonRestProcedure.setDescription(WarArchiveUtil.getRestDescription(procedure));
jsonRestProcedure.setCharSet(charSet);
jsonRestProcedure.setFullyQualifiedProcedureName(restProcedure.getFullyQualifiedProcedureName());
jsonRestProcedure.setModelName(restProcedure.getModelName());
jsonRestProcedure.setProcedureName(restProcedure.getProcedureName());
jsonRestProcedure.setRestMethod(restProcedure.getRestMethod());
jsonRestProcedure.setUri(restProcedure.getUri());
jsonRestProcedure.setQueryParameterList(queryParameterList);
jsonRestProcedure.setHeaderParameterList(headerParameterList);
// If the parameterCount is greater than the number of parameters passed
// on the URI, we can expect more parameters via an input stream
// so the consumes annotation will need to be set. We will set for XML and JSON methods.
boolean hasInputStream = false;
if (uriParameterCount + headerParameterList.size() < parameterCount &&
queryParameterList.size() + headerParameterList.size() < parameterCount) {
hasInputStream = true;
restProcedure.setConsumesAnnotation("@Consumes( MediaType.APPLICATION_XML )"); //$NON-NLS-1$
jsonRestProcedure.setConsumesAnnotation("@Consumes( MediaType.APPLICATION_JSON )"); //$NON-NLS-1$
}
if (hasReturn) {
restProcedure.setProducesAnnotation("@Produces( MediaType.APPLICATION_XML+"+"\"; charset="+charSet+"\")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
jsonRestProcedure.setProducesAnnotation("@Produces( MediaType.APPLICATION_JSON+"+"\"; charset="+charSet+"\")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
restfulProcedureArray.add(restProcedure);
// Only add JSON operation if there is a return or InputStream. Otherwise, one method will do.
if (hasReturn || hasInputStream) {
restfulProcedureArray.add(jsonRestProcedure);
}
}
}
}
}