/*
* 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.ode.integration.store;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ode.bpel.dd.TDeployment;
import org.apache.ode.bpel.iapi.ProcessState;
import org.apache.ode.bpel.iapi.ProcessConf;
import org.apache.ode.bpel.common.InstanceFilter;
import org.apache.ode.bpel.engine.BpelDatabase;
import org.apache.ode.bpel.dao.BpelDAOConnection;
import org.apache.ode.bpel.dao.ProcessInstanceDAO;
import org.apache.ode.bpel.pmapi.ManagementException;
import org.apache.ode.utils.DOMUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.wso2.carbon.bpel.ode.integration.BPELServerImpl;
import org.wso2.carbon.bpel.internal.BPELServiceComponent;
import javax.xml.namespace.QName;
import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* Utility methods for the process store
*/
public class Utils {
private static Log log = LogFactory.getLog(Utils.class);
private static final String HEXES = "0123456789ABCDEF";
private Utils() {
}
public static final Comparator<String> BY_VERSION =
new Comparator<String>() {
public int compare(String o1, String o2) {
String[] nameParts1 = o1.split("/");
String version1 = nameParts1[0].substring(nameParts1[0].lastIndexOf("-") + 1);
String[] nameParts2 = o2.split("/");
String version2 = nameParts2[0].substring(nameParts2[0].lastIndexOf("-") + 1);
return Integer.parseInt(version1) - Integer.parseInt(version2);
}
};
/**
* Create a property mapping based on the initial values in the deployment descriptor.
*
* @param properties properties
* @param dd deployment descriptor
* @return property map
*/
public static Map<QName, Node> calcInitialProperties(final Properties properties,
final TDeployment.Process dd) {
HashMap<QName, Node> ret = new HashMap<QName, Node>();
for (Object key1 : properties.keySet()) {
String key = (String) key1;
Document doc = DOMUtils.newDocument();
doc.appendChild(doc.createElementNS(null, "temporary-simple-type-wrapper"));
doc.getDocumentElement().appendChild(doc.createTextNode(properties.getProperty(key)));
ret.put(new QName(key), doc.getDocumentElement());
}
if (dd.getPropertyList().size() > 0) {
for (TDeployment.Process.Property property : dd.getPropertyList()) {
Element elementContent = DOMUtils.getElementContent(property.getDomNode());
if (elementContent != null) {
// We'll need DOM Level 3
Document doc = DOMUtils.newDocument();
doc.appendChild(doc.importNode(elementContent, true));
ret.put(property.getName(), doc.getDocumentElement());
} else {
ret.put(property.getName(), property.getDomNode().getFirstChild());
}
}
}
return ret;
}
/**
* Figure out the initial process state from the state in the deployment descriptor.
*
* @param dd deployment descriptor
* @return process state
*/
public static ProcessState calcInitialState(final TDeployment.Process dd) {
ProcessState state = ProcessState.ACTIVE;
if (dd.isSetActive() && !dd.getActive()) {
state = ProcessState.DISABLED;
}
if (dd.isSetRetired() && dd.getRetired()) {
state = ProcessState.RETIRED;
}
return state;
}
public static QName toPid(final QName processType, final long version) {
return new QName(processType.getNamespaceURI(), processType.getLocalPart() + "-" + version);
}
public static QName getProcessType(final TDeployment.Process processDescriptor) {
return processDescriptor.getType() != null ? processDescriptor.getType() :
processDescriptor.getName();
}
/**
* Extract BPEL archive to tenant's BPEL file system repository.
*
* @param deploymentContext BPEL deployment information
* @throws Exception on BPEL archive extraction error
*/
public static void extractBPELArchive(final BPELDeploymentContext deploymentContext)
throws Exception {
try {
ZipInputStream zipStream = new ZipInputStream(
new FileInputStream(deploymentContext.getBpelArchive()));
ZipEntry entry;
while ((entry = zipStream.getNextEntry()) != null) {
if (entry.isDirectory()) {
if (log.isDebugEnabled()) {
log.debug("Extracting directory " + entry.getName());
}
if (!new File(deploymentContext.getBpelPackageLocationInFileSystem(),
entry.getName()).mkdirs()) {
throw new Exception("Archive extraction failed. Cannot create directory: "
+ new File(deploymentContext.getBpelPackageLocationInFileSystem(),
entry.getName()).getAbsolutePath() + ".");
}
continue;
}
if (log.isDebugEnabled()) {
log.debug("Extracting file " + entry.getName());
}
File destFile = new File(deploymentContext.getBpelPackageLocationInFileSystem(),
entry.getName());
if (!destFile.getParentFile().exists()) {
if (!destFile.getParentFile().mkdirs()) {
throw new Exception("Archive extraction failed. Cannot create directory: "
+ destFile.getParentFile().getAbsolutePath());
}
}
copyInputStream(zipStream,
new BufferedOutputStream(new FileOutputStream(destFile)));
}
zipStream.close();
deploymentContext.setBPELPackageContent(
new File(deploymentContext.getBpelPackageLocationInFileSystem()));
} catch (IOException e) {
String errMsg = "Error occurred during extracting the archive: " +
deploymentContext.getArchiveName();
log.error(errMsg, e);
throw new Exception(errMsg, e);
}
}
private static void copyInputStream(final InputStream in, final OutputStream out)
throws IOException {
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) >= 0) {
out.write(buffer, 0, len);
}
out.close();
}
public static void deleteInstances(Collection<QName> processes) {
if (processes != null) {
String filter = null;
for (QName q : processes) {
if (filter == null) {
filter = "pid=" + q.toString();
} else {
filter += "|" + q.toString();
}
}
List<Long> iids = delete(filter);
logIids(processes, iids);
} else {
//This very much unlikely to happen. How to handle this situation?
log.warn("No processes found to delete instances");
}
}
public static List<Long> delete(String filter) {
log.info("Instance filter for instance deletion:" + filter);
final InstanceFilter instanceFilter = new InstanceFilter(filter);
final List<Long> ret = new LinkedList<Long>();
try {
dbexec(new BpelDatabase.Callable<Object>() {
public Object run(BpelDAOConnection conn) {
Collection<ProcessInstanceDAO> instances = conn.instanceQuery(instanceFilter);
for (ProcessInstanceDAO instance : instances) {
instance.delete(EnumSet.allOf(ProcessConf.CLEANUP_CATEGORY.class), true);
ret.add(instance.getInstanceId());
}
return null;
}
});
} catch (Exception e) {
String errMsg = "Exception during instance deletion. Filter: " + filter;
log.error(errMsg, e);
throw new ManagementException(errMsg, e);
}
return ret;
}
private static void logIids(Collection<QName> pids, List<Long> ids) {
if (log.isDebugEnabled()) {
log.debug("Deleting instances of processes: ");
for (QName q : pids) {
log.debug(q);
}
log.debug("Instance IDs:");
for (Long l : ids) {
log.debug(l);
}
}
}
/**
* Execute a database transaction, unwrapping nested
* {@link org.apache.ode.bpel.pmapi.ManagementException}s.
*
* @param callable action to run
* @return object of type T
* @throws org.apache.ode.bpel.pmapi.ManagementException
* if exception occurred during transaction
*/
private static <T> T dbexec(BpelDatabase.Callable<T> callable) throws ManagementException {
try {
BPELServerImpl bpelServer = (BPELServerImpl) BPELServiceComponent.getBPELServer();
BpelDatabase bpelDb = bpelServer.getODEBPELServer().getBpelDb();
return bpelDb.exec(callable);
} catch (ManagementException me) {
// Passthrough.
throw me;
} catch (Exception ex) {
log.error("Exception during database operation", ex);
throw new ManagementException("Exception during database operation", ex);
}
}
public static String getHex(byte[] raw) {
if (raw == null) {
return null;
}
final StringBuilder hex = new StringBuilder(2 * raw.length);
for (final byte b : raw) {
hex.append(HEXES.charAt((b & 0xF0) >> 4))
.append(HEXES.charAt((b & 0x0F)));
}
return hex.toString();
}
public static byte[] createChecksum(File fileToCalculateMD5)
throws IOException, NoSuchAlgorithmException {
InputStream fis = new FileInputStream(fileToCalculateMD5);
byte[] buffer = new byte[1024];
MessageDigest complete = MessageDigest.getInstance("MD5");
int numRead;
do {
numRead = fis.read(buffer);
if (numRead > 0) {
complete.update(buffer, 0, numRead);
}
} while (numRead != -1);
fis.close();
return complete.digest();
}
// see this How-to for a faster way to convert
// a byte array to a HEX string
public static String getMD5Checksum(File file) throws IOException, NoSuchAlgorithmException {
byte[] b = createChecksum(file);
return getHex(b);
}
}