package org.joget.workflow.util; import java.awt.Container; import java.awt.Graphics2D; import java.awt.Image; import java.awt.MediaTracker; import java.awt.RenderingHints; import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.List; import javax.imageio.ImageIO; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.ssl.SSLContextBuilder; import org.joget.commons.util.DynamicDataSourceManager; import org.joget.commons.util.LogUtil; import org.joget.commons.util.SetupManager; import org.joget.workflow.model.WorkflowProcess; import org.joget.workflow.model.service.WorkflowManager; import org.springframework.context.ApplicationContext; import org.springframework.core.task.TaskExecutor; /** * Utility methods used to generate XPDl image * */ public class XpdlImageUtil { public static final String IMAGE_FOLDER = "app_xpdlImages"; public static final String IMAGE_EXTENSION = ".png"; public static final String THUMBNAIL_PREFIX = "thumb-"; public static final int THUMBNAIL_SIZE = 400; /** * Gets the XPDL image path in wflow/app_xpdlImages folder * @param processDefId * @return */ public static String getXpdlImagePath(String processDefId) { ApplicationContext appContext = WorkflowUtil.getApplicationContext(); WorkflowManager workflowManager = (WorkflowManager) appContext.getBean("workflowManager"); WorkflowProcess process = workflowManager.getProcess(processDefId); File path = new File(SetupManager.getBaseDirectory(), IMAGE_FOLDER + File.separator + process.getPackageId() + File.separator); return path.getAbsolutePath(); } /** * Gets the XPDL image. * @param designerwebBaseUrl * @param processDefId * @return */ public static File getXpdlImage(String designerwebBaseUrl, String processDefId) { File file = new File(getXpdlImagePath(processDefId), processDefId + IMAGE_EXTENSION); if (!file.exists()) { generateXpdlImage(designerwebBaseUrl, processDefId); file = new File(getXpdlImagePath(processDefId), processDefId + IMAGE_EXTENSION); } return file; } /** * Gets the XPDL image thumbnail. * @param designerwebBaseUrl * @param processDefId * @return */ public static File getXpdlThumbnail(String designerwebBaseUrl, String processDefId) { File file = new File(getXpdlImagePath(processDefId), THUMBNAIL_PREFIX + processDefId + IMAGE_EXTENSION); if (!file.exists()) { generateXpdlImage(designerwebBaseUrl, processDefId); file = new File(getXpdlImagePath(processDefId), THUMBNAIL_PREFIX + processDefId + IMAGE_EXTENSION); } return file; } /** * Queue a task for XPDL image generation * * @deprecated this is not used in v5 since the Workflow Designer is replaced * by a web-based Process Builder * * @param designerwebBaseUrl * @param processDefId */ public static void generateXpdlImage(final String designerwebBaseUrl, final String processDefId) { generateXpdlImage(designerwebBaseUrl, processDefId, false); } /** * Queue a task for XPDL image generation * * @deprecated this is not used in v5 since the Workflow Designer is replaced * by a web-based Process Builder * * @param designerwebBaseUrl * @param processDefId * @param asynchronous */ public static void generateXpdlImage(final String designerwebBaseUrl, final String processDefId, boolean asynchronous) { String profile = DynamicDataSourceManager.getCurrentProfile(); TaskExecutor executor = (TaskExecutor) WorkflowUtil.getApplicationContext().getBean("xpdlImageExecutor"); executor.execute(new XpdlImageTask(profile, designerwebBaseUrl, processDefId)); } /** * Create the XDPL image * * @deprecated this is not used in v5 since the Workflow Designer is replaced * by a web-based Process Builder * * @param designerwebBaseUrl * @param processDefId */ public static void createXpdlImage(String designerwebBaseUrl, String processDefId) { String baseDir = getXpdlImagePath(processDefId); ApplicationContext appContext = WorkflowUtil.getApplicationContext(); WorkflowManager workflowManager = (WorkflowManager) appContext.getBean("workflowManager"); WorkflowProcess process = workflowManager.getProcess(processDefId); byte[] xpdlBytes = workflowManager.getPackageContent(process.getPackageId(), process.getVersion()); FileOutputStream fos = null; CloseableHttpClient httpClient = null; try { String fileName = processDefId + IMAGE_EXTENSION; File file = new File(baseDir); file.mkdirs(); file = new File(baseDir, fileName); ByteArrayOutputStream bos = new ByteArrayOutputStream(); String url = designerwebBaseUrl + "/viewer/viewer.jsp?processId=" + process.getEncodedId(); URL urlObj = new URL(url); HttpClientBuilder httpClientBuilder = HttpClients.custom(); HttpPost post = new HttpPost(url); List<NameValuePair> data = new ArrayList<NameValuePair>(); data.add(new BasicNameValuePair("xpdl", new String(xpdlBytes, "UTF-8"))); data.add(new BasicNameValuePair("packageId", process.getPackageId())); data.add(new BasicNameValuePair("processId", processDefId)); post.setEntity(new UrlEncodedFormEntity(data)); post.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); if ("https".equals(urlObj.getProtocol())) { SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(), NoopHostnameVerifier.INSTANCE); httpClientBuilder.setSSLSocketFactory(sslsf); } // execute request httpClient = httpClientBuilder.build(); HttpResponse response = httpClient.execute(post); HttpEntity entity = response.getEntity(); InputStream is = entity.getContent(); try { byte[] buffer = new byte[1024]; int byteReaded = is.read(buffer); while (byteReaded != -1) { bos.write(buffer, 0, byteReaded); byteReaded = is.read(buffer); } bos.flush(); // output to file byte[] contents = bos.toByteArray(); ByteArrayInputStream bis = new ByteArrayInputStream(contents); fos = new FileOutputStream(file); buffer = new byte[1024]; byteReaded = bis.read(buffer); while (byteReaded != -1) { fos.write(buffer, 0, byteReaded); byteReaded = bis.read(buffer); } fos.flush(); } finally { if (is != null) { is.close(); } } createThumbnail(baseDir, processDefId); } catch (Exception ex) { LogUtil.error(XpdlImageUtil.class.getName(), ex, "Error generating xpdl image [processDefId=" + processDefId + "]"); } finally { try { if (fos != null) { fos.close(); } } catch (Exception ex) { LogUtil.error(XpdlImageUtil.class.getName(), ex, ""); } try { if (httpClient != null) { httpClient.close(); } } catch (Exception ex) { LogUtil.error(XpdlImageUtil.class.getName(), ex, ""); } } } /** * Create the XPDL image thumbnail * * @deprecated this is not used in v5 since the Workflow Designer is replaced * by a web-based Process Builder * * @param path * @param processDefId */ public static void createThumbnail(String path, String processDefId) { int thumbWidth = THUMBNAIL_SIZE; int thumbHeight = THUMBNAIL_SIZE; BufferedOutputStream out = null; try{ Image image = Toolkit.getDefaultToolkit().getImage(new File(path, processDefId + IMAGE_EXTENSION).getAbsolutePath()); MediaTracker mediaTracker = new MediaTracker(new Container()); mediaTracker.addImage(image, 0); mediaTracker.waitForID(0); double thumbRatio = (double) thumbWidth / (double) thumbHeight; int imageWidth = image.getWidth(null); int imageHeight = image.getHeight(null); double imageRatio = (double) imageWidth / (double) imageHeight; if (thumbRatio < imageRatio) { thumbHeight = (int) (thumbWidth / imageRatio); } else { thumbWidth = (int) (thumbHeight * imageRatio); } BufferedImage thumbImage = new BufferedImage(thumbWidth, thumbHeight, BufferedImage.TYPE_INT_RGB); Graphics2D graphics2D = thumbImage.createGraphics(); graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); graphics2D.drawImage(image, 0, 0, thumbWidth, thumbHeight, null); out = new BufferedOutputStream(new FileOutputStream(new File(path, THUMBNAIL_PREFIX + processDefId + IMAGE_EXTENSION))); ImageIO.write(thumbImage, "png", out); out.flush(); } catch (Exception ex) { LogUtil.error(XpdlImageUtil.class.getName(), ex, "Error generating xpdl thumbnail [processDefId=" + processDefId + "]"); } finally { try { if (out != null) { out.close(); } } catch (Exception ex) { LogUtil.error(XpdlImageUtil.class.getName(), ex, ""); } } } }