/*
* Copyright 2012 SAP AG
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.sap.research.connectivity.gw;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import org.apache.commons.io.IOUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.springframework.roo.classpath.PhysicalTypeCategory;
import org.springframework.roo.classpath.PhysicalTypeIdentifier;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetailsBuilder;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.file.monitor.event.FileDetails;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.LogicalPath;
import org.springframework.roo.project.Path;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import com.sap.research.connectivity.gw.parsers.JavaSourceField;
import com.sap.research.connectivity.gw.parsers.JavaSourceFieldBuilder;
import com.sap.research.connectivity.gw.parsers.JavaSourceFileEditor;
import com.sap.research.connectivity.gw.parsers.JavaSourceMethod;
import com.sap.research.connectivity.gw.parsers.JavaSourceMethodBuilder;
import com.sap.research.connectivity.gw.parsers.MetadataXMLParser;
/**
* Implementation of {@link GwOperations} interface.
*
* @since 1.1.1
*/
@Component
@Service
public class GwOperationsImpl extends GWOperationsUtils implements GwOperations {
/* ------------------------------------------------------------------------------------------------------------------
* SAP RESEARCH OWN CODE
*
*
*/
//GW Setup command is available only if project and persistence are setup
public boolean isCommandGWSetupAvailable() {
return isInstalledInModule(projectOperations.getFocusedModuleName());
}
public boolean isInstalledInModule(final String moduleName) {
final LogicalPath resourcesPath = LogicalPath.getInstance(
Path.SRC_MAIN_RESOURCES, moduleName);
// System.out.println(moduleName + resourcesPath.toString());
return isProjectAvailable()
&& fileManager.exists(projectOperations.getPathResolver()
.getIdentifier(resourcesPath, PERSISTENCE_XML));
}
public boolean isProjectAvailable() {
return projectOperations.isFocusedProjectAvailable();
}
//Add ODATA Endpoint command is available only if the class for GW Connectivity is present
public boolean isCommandODataEndpointAvailable() {
return fileManager.exists(getSubPackagePath(oDataFolder) + SEPARATOR + "ODataConnectivity.java");
}
//Add GW Entity command is available only if an OData Endpoint class has been defined and thus metadata has been extracted
public boolean isCommandGWEntityAvailable() {
SortedSet<FileDetails> files = fileManager.findMatchingAntPath(getSubPackagePath(oDataFolder) + SEPARATOR + "*_metadata.xml");
return !files.isEmpty();
}
//Add GW Field command is available only if the GW Entity command is available and a java class exists connecting to an ODATA Endpoint
public boolean isCommandGWFieldAvailable() throws IOException {
SortedSet<FileDetails> files = fileManager.findMatchingAntPath(getSubPackagePath(domain) + SEPARATOR + "*.java");
for (FileDetails file : files) {
InputStream inputStream = fileManager.getInputStream(file.getCanonicalPath());
if (IOUtils.toString(inputStream).contains("ODataConnectivity " + GwUtils.GW_CONNECTION_FIELD_NAME)) {
IOUtils.closeQuietly(inputStream);
return true;
}
IOUtils.closeQuietly(inputStream);
}
return false;
}
public boolean isCommandGWMVCAdaptCommandAvailable() throws IOException {
/*
* We check first to see if there is a gateway entity available. If so, we check if there are any generated controllers.
*/
boolean returnResults = false;
if (isCommandGWFieldAvailable() == true) {
SortedSet<FileDetails> files = fileManager.findMatchingAntPath(getSubPackagePath(web) + SEPARATOR + "*Controller.java");
if (!files.isEmpty())
returnResults = true;
}
return returnResults;
}
public void addODataConnectivity() {
addODataDependenciesToPom();
addOdataConnectivityClass();
}
public void addNamespace(String nsName, String url, String user, String pass, String csrfMode, String host, String port, int timeout) throws Exception {
String metadataString = "";
try {
metadataString = getMetadataString(url, user, pass, host, port, timeout);
} catch (Exception e) {
throw new Exception(e.getMessage());
}
if (metadataString.isEmpty())
throw new Exception("The specified URL did not return any valid data!");
Map<String, String> replacements = new HashMap<String, String>();
final String subPackagePath = getSubPackagePath(oDataFolder);
final String topLevelPackageName = getTopLevelPackageName();
replacements.put("<<PACKAGE>>", "package " + topLevelPackageName + "." + oDataFolder + ";\n");
replacements.put("<<NSNAME>>", nsName);
replacements.put("<<URL>>", url);
replacements.put("<<USER>>", user);
replacements.put("<<PASSWORD>>", pass);
if (csrfMode.equals("standard")) {
replacements.put("<<CSRF_MODE_GET>>", ".header(\"X-CSRF-Token\", \"Fetch\")");
replacements.put("<<CSRF_MODE_SET>>", ".header(\"X-CSRF-Token\", this.xsrfTokenValue).header(\"Cookie\", xsrfCookieName + \"=\" + xsrfCookieValue)");
}
else {
replacements.put("<<CSRF_MODE_GET>>", "");
replacements.put("<<CSRF_MODE_SET>>", ".header(\"X-Requested-With\", \"XMLHttpRequest\")");
}
if(host == null && port == null){
replacements.put("<<HOST>>", "");
replacements.put("<<PORT>>", "");
} else {
replacements.put("<<HOST>>", host);
replacements.put("<<PORT>>", port);
}
GwUtils.createClassFileFromTemplate(topLevelPackageName,
subPackagePath,
"ODataNS_template.java",
nsName + ".java",
replacements,
fileManager,
getClass());
GwUtils.createFileFromString(subPackagePath,
nsName + "_metadata.xml",
metadataString,
fileManager);
}
public void createEntity(final String endpointName, final String remoteEntitySetName) throws Exception {
final String subPackagePath = getSubPackagePath(oDataFolder);
// Verify if the endpoint and remote entity set are valid
if (!fileManager.exists(subPackagePath + SEPARATOR + endpointName + "_metadata.xml")) {
throw new Exception("Namespace \"" + endpointName + "\" does not exist or is corrupted. Please specify a valid namespace.");
}
else {
InputStream metaDataIs = fileManager.getInputStream(subPackagePath + SEPARATOR + endpointName +"_metadata.xml");
Document doc = XmlUtils.getDocumentBuilder().parse(metaDataIs);
NodeList nodeList = doc.getElementsByTagName("entity");
boolean remoteEntityExists = false;
for (int i = 0; i < nodeList.getLength(); i++){
Attr attr = (Attr) nodeList.item(i).getAttributes().getNamedItem("name");
if (attr.getValue().toString().equals(remoteEntitySetName)) {
remoteEntityExists = true;
break;
}
}
if (!remoteEntityExists) {
throw new Exception("Remote entity set \"" + remoteEntitySetName + "\" does not exist. Please specify a valid remote entity.");
}
}
// Create entity class and add JPA Annotations.
int modifier = Modifier.PUBLIC;
String localEntityPath = typeLocationService.getTopLevelPackageForModule(projectOperations.getFocusedModule()) +
"." + domain +
"." + remoteEntitySetName;
JavaType localEntity = new JavaType(localEntityPath);
final String declaredByMetadataId = PhysicalTypeIdentifier
.createIdentifier(localEntity,
pathResolver.getFocusedPath(Path.SRC_MAIN_JAVA));
final ClassOrInterfaceTypeDetailsBuilder cidBuilder = new ClassOrInterfaceTypeDetailsBuilder(
declaredByMetadataId, modifier, localEntity,
PhysicalTypeCategory.CLASS);
final List<AnnotationMetadataBuilder> annotationBuilder = new ArrayList<AnnotationMetadataBuilder>();
annotationBuilder.add(ROO_JAVA_BEAN_BUILDER);
annotationBuilder.add(ROO_TO_STRING_BUILDER);
annotationBuilder.add(ROO_JPA_ACTIVE_RECORD_BUILDER);
cidBuilder.setAnnotations(annotationBuilder);
typeManagementService.createOrUpdateTypeOnDisk(cidBuilder.build());
}
public void addFieldsMethodsAndRelations(final String namespace, String remoteEntity, boolean importAll, boolean importAssociations) throws Exception{
// Extract fields and keys from Metadata XML
Map<String[], String> fields = new HashMap<String[], String>();
Map<String[], String> keys = new HashMap<String[], String>();
Map<String[], String> allFields = new HashMap<String[], String>();
MetadataXMLParser xmlParser = getXMLParser(namespace, remoteEntity);
xmlParser.parse();
// Get handler for File Editor to edit the entity file
JavaSourceFileEditor entityClassFile = getJavaFileEditor(domain, remoteEntity);
// Add Imports
addImports(entityClassFile, namespace);
if (importAll)
fields = xmlParser.getFields();
keys = xmlParser.getKeys();
// Add Field declaration for OData Connector
JavaSourceField odc = new JavaSourceFieldBuilder()
.fieldPrefix("private final static")
.fieldType("ODataConnectivity")
.fieldName(GwUtils.GW_CONNECTION_FIELD_NAME)
.fieldValue("new " + namespace + "()")
.build();
entityClassFile.addGlobalField(odc);
// Add Fields and corresponding getter/setters methods to the Entity Class
Map<String[], String> keysIncludingId = new HashMap<String[], String>();
// Include id as a key(type String)
String[] idKeyName = {"Id", "Id"};
keysIncludingId.put(idKeyName, "String");
keysIncludingId.putAll(keys);
addGatewayFields(keysIncludingId, fields, entityClassFile);
// Add Persistence Methods
// Send all the fields (keys plus fields)
allFields.putAll(keys);
allFields.putAll(fields);
addPersistenceMethods(allFields, entityClassFile, remoteEntity, keys);
// Add relationships
if (importAssociations) {
Map<String, String[]> relationships = new HashMap<String, String[]>();
relationships = xmlParser.getRelationships();
addRelationships(relationships, remoteEntity, entityClassFile);
}
entityClassFile.makeFile();
// throw new Exception(entityClassFile.getFileContent());
}
private MetadataXMLParser getXMLParser(final String namespace, String remoteEntity) throws IllegalStateException {
String metaDataPath = getSubPackagePath(oDataFolder);
String metaDataFile = metaDataPath + SEPARATOR + namespace +"_metadata.xml";
InputStream metaDataIs = fileManager.getInputStream(metaDataFile);
Document doc;
try {
doc = XmlUtils.getDocumentBuilder().parse(metaDataIs);
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
MetadataXMLParser xmlParser = new MetadataXMLParser(doc, remoteEntity);
return xmlParser;
}
public void modifyController(final String remoteEntity) throws Exception{
// Get handler for File Editor to edit the entity file
String controllerEntityName = remoteEntity + "Controller";
JavaSourceFileEditor controllerClassFile = getJavaFileEditor(web, controllerEntityName);
addControllerImports(controllerClassFile);
/*
* We need to overwrite the show method of the .aj file, as we need to pass the itemId to the model as URLEncoded
*/
JavaSourceMethod showMethod = new JavaSourceMethodBuilder()
.methodName("show")
.methodPrefix("public")
.returnType("String")
.annotations("@RequestMapping(value = \"/{Id}\", produces = \"text/html\")")
.parameters(getControllerShowMethodParameters())
.methodBody(getControllerShowMethodBody(remoteEntity))
.build();
controllerClassFile.addMethod(showMethod);
controllerClassFile.makeFile();
}
public void addRemoteFieldInGWClass(String localClassName, String fieldName) throws Exception {
// Get handler for File Editor to edit (and search) the entity file
JavaSourceFileEditor entityClassFile = getJavaFileEditor(domain, localClassName);
Map.Entry<String[], String> fieldObj = getValidatedField(localClassName, fieldName, entityClassFile);
if (fieldObj == null)
throw new Exception("The name \"" + fieldName + "\" is not a valid name. Please choose a name from the provided list.");
addRemoteFieldInGWJavaFile(entityClassFile, fieldObj);
addRemoteFieldInPersistenceMethods(entityClassFile, fieldObj);
entityClassFile.makeFile();
}
public void addLocalFieldInGWClass(String localClassName, String fieldName, JavaType fieldType) throws Exception {
// Get handler for File Editor to edit (and search) the entity file
JavaSourceFileEditor entityClassFile = getJavaFileEditor(domain, localClassName);
Map.Entry<String[], String> fieldObj = getValidatedField(localClassName, fieldName, entityClassFile);
if (fieldObj != null)
throw new Exception("The name \"" + fieldName + "\" is a valid name for a remote field. In order to reduce confusions, " +
"please choose another name for your local field.");
String processedTypeName = fieldType.getNameIncludingTypeParameters();
processedTypeName = processedTypeName.substring(processedTypeName.lastIndexOf(".") + 1);
addFieldInGWJavaFile(entityClassFile, fieldName, processedTypeName);
addLocalFieldInPersistenceMethods(entityClassFile, fieldName, processedTypeName);
entityClassFile.makeFile();
}
}