/*
* Copyright (c) 2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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 org.wso2.carbon.bpel.ui.fileupload;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ode.bpel.dd.DeployDocument;
import org.apache.ode.bpel.dd.TDeployment;
import org.apache.ode.bpel.dd.TInvoke;
import org.apache.ode.bpel.dd.TProvide;
import org.apache.ode.store.DeploymentUnitDir;
import org.wso2.carbon.CarbonConstants;
import org.wso2.carbon.CarbonException;
import org.wso2.carbon.ui.transports.fileupload.AbstractFileUploadExecutor;
import org.wso2.carbon.utils.FileItemData;
import org.wso2.carbon.utils.ServerConstants;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.namespace.QName;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
*
*/
public class BPELUploadExecutor extends AbstractFileUploadExecutor {
private static Log log = LogFactory.getLog(BPELUploadExecutor.class);
private static final String[] ALLOWED_FILE_EXTENSIONS =
new String[]{".zip"};
private String tmpDir = "bpelTemp";
private static final int BUFFER = 2048;
public boolean execute(HttpServletRequest request,
HttpServletResponse response) throws CarbonException, IOException {
String errMsg;
response.setContentType("text/html; charset=utf-8");
PrintWriter out = response.getWriter();
String serverURL = (String) request.getAttribute(CarbonConstants.SERVER_URL);
String cookie = (String) request.getAttribute(ServerConstants.ADMIN_SERVICE_COOKIE);
if (fileItemsMap == null || fileItemsMap.isEmpty()) {
String msg = "File uploading failed.";
log.error(msg);
out.write("<textarea>" +
"(function(){i18n.fileUplodedFailed();})();" +
"</textarea>");
}
BPELUploaderClient uploaderClient = new BPELUploaderClient(configurationContext,
serverURL + "BPELUploader", cookie);
try {
for (Object o : fileItemsMap.keySet()) {
String fieldName = (String) o;
ArrayList<FileItemData> fileItemDataList = fileItemsMap.get(fieldName);
for (FileItemData fileItemData : fileItemDataList) {
String fileName = getFileName(fileItemData.getFileItem().getName());
//Check filename for \ charactors. This cannot be handled at the lower stages.
if (fileName.matches("(.*[\\\\].*[/].*|.*[/].*[\\\\].*)")) {
log.error("BPEL Package Validation Failure: one or many of the following illegal characters are in " +
"the package.\n ~!@#$;%^*()+={}[]| \\<>");
throw new Exception("BPEL Package Validation Failure: one or many of the following illegal characters " +
"are in the package. ~!@#$;%^*()+={}[]| \\<>");
}
//Check file extension.
checkServiceFileExtensionValidity(fileName, ALLOWED_FILE_EXTENSIONS);
if (fileName.lastIndexOf("\\") != -1) {
int indexOfColon = fileName.lastIndexOf("\\") + 1;
fileName = fileName.substring(indexOfColon, fileName.length());
}
if (fieldName.equals("bpelFileName")) {
SaveExtractReturn uploadedFiles = saveAndExtractUploadedFile(fileItemData.getFileItem());
validateBPELPackage(uploadedFiles.extractedFile);
DataSource dataSource = new FileDataSource(uploadedFiles.zipFile);
uploaderClient.addUploadedFileItem(new DataHandler(dataSource), fileName, "zip");
}
}
}
uploaderClient.uploadFileItems();
out.write("<textarea>" +
"(function(){i18n.bpelPckgUplodedSuccess();})();" +
"</textarea>");
return true;
} catch (Exception e) {
errMsg = "BPEL Package Deployment Failed!" + e.getMessage();
log.error(errMsg);
out.write("<textarea>" +
"(function(){i18n.bpelPckgUplodedFailed('" + e.getMessage() + "');})();" +
"</textarea>");
}
return false;
}
public SaveExtractReturn saveAndExtractUploadedFile(FileItem fileItem) throws Exception {
String serviceUploadDir = getTempUploadDir();
File servicesDir = new File(serviceUploadDir);
servicesDir.mkdirs();
// Writing BPEL archive to file system
String fileItemName = getFileName(fileItem.getName());
File uploadedFile = new File(servicesDir, fileItemName);
if (log.isDebugEnabled()) {
log.debug("[BPELUI]BPEL Archive Path: " + uploadedFile.getAbsolutePath());
}
try {
fileItem.write(uploadedFile);
} catch (Exception e) {
log.error("Erorr occurred while writing file item to file system.", e);
throw new Exception("Erorr occurred while writing file item to file system.", e);
}
String destinationDir = serviceUploadDir + File.separator + fileItemName.substring(0, fileItemName.lastIndexOf("."));
if (log.isDebugEnabled()) {
log.debug("[BPELUI]Bpel package location: " + destinationDir);
}
try {
ArchiveExtractor.extract(uploadedFile, destinationDir);
} catch (Exception e) {
log.error("Error extracting archive.", e);
throw new Exception(e);
}
// Handling backward compatibility issues. If user upload BPEL archive which follows the BPS 1.0.1 archive format
// we need to convert it to new format and upload.
File deployXml = new File(destinationDir, "deploy.xml");
if (!deployXml.exists()) {
String depXmlSrc = fileItemName.substring(0, fileItemName.lastIndexOf(".")) + File.separator + "deploy.xml";
deployXml = new File(destinationDir, depXmlSrc);
if (deployXml.exists() &&
onlyOneChildDir(destinationDir, fileItemName.substring(0, fileItemName.lastIndexOf(".")))) {
String tempUploadDir = getTempUploadDir();
File tempDir = new File(tempUploadDir);
tempDir.mkdirs();
String filesToZipParent = destinationDir + File.separator + fileItemName.substring(0, fileItemName.lastIndexOf("."));
String zipLocation = tempDir.getAbsolutePath() + File.separator + fileItemName;
try {
zip(zipLocation, filesToZipParent);
} catch (Exception e) {
throw new Exception(e);
}
return new SaveExtractReturn(zipLocation, filesToZipParent);
}
throw new Exception("BPEL Archive format error.");
}
return new SaveExtractReturn(uploadedFile.getAbsolutePath(), destinationDir);
}
private void zip(String zipFile, String sourceDir) throws Exception {
File dirObj = new File(sourceDir);
int len = dirObj.getAbsolutePath().length() + 1;
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile));
if(log.isDebugEnabled()){
log.debug("Creating: " + zipFile);
}
addDir(dirObj, out, len);
out.close();
}
private static void addDir(File dirObj, ZipOutputStream out, int basePathLen) throws Exception{
File[] files = dirObj.listFiles();
byte[] tmpBuf = new byte[2048];
for (File file : files) {
if (file.isDirectory()) {
addDir(file, out, basePathLen);
continue;
}
FileInputStream in = null;
try {
in = new FileInputStream(file.getAbsolutePath());
if (log.isDebugEnabled()) {
log.debug("Adding: " + file.getAbsolutePath());
}
out.putNextEntry(new ZipEntry(file.getAbsolutePath().substring(basePathLen)));
int len;
while ((len = in.read(tmpBuf)) > 0) {
out.write(tmpBuf, 0, len);
}
} finally {
if (in != null) {
in.close();
}
}
}
}
private String getTempUploadDir() {
String uuid = generateUUID();
return getWorkingDir() + File.separator + this.tmpDir + File.separator + uuid + File.separator;
}
private boolean onlyOneChildDir(String location, String dirNameToCheck) {
File parentDir = new File(location);
if (parentDir.isDirectory()) {
String[] entries = parentDir.list();
if (entries.length == 1 && entries[0].equals(dirNameToCheck)) {
return true;
}
}
return false;
}
public void validateBPELPackage(String directoryPath) throws Exception {
DeploymentUnitDir du;
try {
du = new DeploymentUnitDir(new File(directoryPath));
} catch (IllegalArgumentException iae) {
log.error("BPEL Package Validation Failure.", iae);
throw new Exception("BPEL Package Validation Failure.");
}
//check package for illegal charactors which registry does not support. (~!@#$;%^*()+={}[]|\<>)
List<File> packageFiles = du.allFiles();
for (File packageFile : packageFiles) {
if (!packageFile.getName().matches("[^\\~\\!\\@\\#\\$\\;\\%\\^\\*\\(\\)\\+ /\\=\\{\\}\\[\\]\\\\|\\<\\>\"\\'\\`]+")) {
log.error("BPEL Package Validation Failure: one or many of the following illegal characters are in " +
"the package.\n ~!@#$;%^*()+={}[]| \\<>\"'`");
throw new Exception("BPEL Package Validation Failure: one or many of the following illegal characters " +
"are in the package. ~!@#$;%^*()+={}[]| \\<>\"'`");
}
}
try {
du.compile();
} catch (RuntimeException ce) {
log.error("BPEL Process Compilation Failure.", ce);
throw new Exception("BPEL Compilation Failure!");
} catch (Exception e) {
log.error("BPEL Process Compilation Failure.", e);
throw new Exception("BPEL Compilation Failure!");
}
du.scan();
DeployDocument dd = du.getDeploymentDescriptor();
for(TDeployment.Process processDD : dd.getDeploy().getProcessList()){
QName processType = processDD.getType() != null ? processDD.getType() : processDD.getName();
DeploymentUnitDir.CBPInfo cbpInfo = du.getCBPInfo(processType);
if (cbpInfo == null) {
//removeDeploymentArtifacts(deploymentContext, du);
String logMessage = "Aborting deployment. Cannot find Process definition for type "
+ processType + ".";
log.error(logMessage);
throw new Exception(logMessage);
}
for (TProvide tProvide : processDD.getProvideList()) {
if (tProvide.getService() == null) {
String errMsg = "Service element missing for the provide element in deploy.xml";
log.error(errMsg);
throw new Exception(errMsg);
}
}
for (TInvoke tInvoke : processDD.getInvokeList()) {
if (tInvoke.getService() == null) {
String errMsg = "Service element missing for the invoke element in deploy.xml";
log.error(errMsg);
throw new Exception(errMsg);
}
}
}
}
class SaveExtractReturn {
private String zipFile;
private String extractedFile;
public SaveExtractReturn(String zipFile, String extractedFile) {
this.zipFile = zipFile;
this.extractedFile = extractedFile;
}
}
}