diff --git a/.gitignore b/.gitignore index 979f91d..376dc0a 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,7 @@ local.properties .classpath .settings/ .loadpath - +.vscode/ # External tool builders .externalToolBuilders/ diff --git a/pom.xml b/pom.xml index ae244df..53d7dbe 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ org.joget wflow-core - 7.0-SNAPSHOT + 8.1-SNAPSHOT javax.servlet diff --git a/src/main/java/org/joget/marketplace/EnhancedJsonTool.java b/src/main/java/org/joget/marketplace/EnhancedJsonTool.java index f9f6f7e..f0358e5 100644 --- a/src/main/java/org/joget/marketplace/EnhancedJsonTool.java +++ b/src/main/java/org/joget/marketplace/EnhancedJsonTool.java @@ -1,12 +1,11 @@ package org.joget.marketplace; import bsh.Interpreter; -import java.io.BufferedReader; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.Date; @@ -65,6 +64,7 @@ import org.apache.http.conn.ConnectTimeoutException; import java.net.SocketTimeoutException; import org.apache.http.impl.conn.SystemDefaultRoutePlanner; +import org.apache.commons.io.IOUtils; public class EnhancedJsonTool extends DefaultApplicationPlugin { @@ -280,7 +280,7 @@ public Object execute(Map properties) { } URL u = new URL(path); CustomURLDataSource c = new CustomURLDataSource(u); - builder.addBinaryBody(parameterName, c.getInputStream().readAllBytes(), ContentType.create(c.getContentType()), c.getName()); + builder.addBinaryBody(parameterName, IOUtils.toByteArray(c.getInputStream()), ContentType.create(c.getContentType()), c.getName()); } } catch (Exception e) { @@ -515,17 +515,13 @@ public Object execute(Map properties) { filePath = filePath + fileName; - FileOutputStream fos = null; - try (InputStream is = response.getEntity().getContent()) { - fos = new FileOutputStream(new File(filePath)); - int inByte; - while ((inByte = is.read()) != -1) { - fos.write(inByte); + try (InputStream is = response.getEntity().getContent(); + FileOutputStream fos = new FileOutputStream(filePath)) { + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = is.read(buffer)) != -1) { + fos.write(buffer, 0, bytesRead); } - } catch (Exception ex) { - LogUtil.error(getClass().getName(), ex, "Cannot save file"); - } finally { - fos.close(); } } } @@ -661,7 +657,8 @@ protected void storeToWorkflowVariable(WorkflowAssignment wfAssignment, Map prop String variable = mapping.get("variable").toString(); String jsonObjectName = mapping.get("jsonObjectName").toString(); - String value = (String) getObjectFromMap(jsonObjectName, object); + Object valueObj = getObjectFromMap(jsonObjectName, object); + String value = (valueObj != null) ? String.valueOf(valueObj) : null; if (value != null) { workflowManager.activityVariable(wfAssignment.getActivityId(), variable, value); @@ -703,7 +700,8 @@ protected Object getObjectFromMap(String key, Map object) { String tempKey = key.substring(0, key.indexOf("[")); int number = Integer.parseInt(key.substring(key.indexOf("[") + 1, key.indexOf("]"))); Object tempObjectArray[] = (Object[]) object.get(tempKey); - if (tempObjectArray != null && tempObjectArray.length > number) { + + if (number >= 0 && tempObjectArray != null && number < tempObjectArray.length) { return tempObjectArray[number]; } } else { @@ -754,33 +752,6 @@ protected FormRow getRow(WorkflowAssignment wfAssignment, String multirowBaseObj return row; } - protected String streamToString(InputStream in) { - try { - BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8")); - StringBuilder sb = new StringBuilder(); - - String line = null; - try { - while ((line = reader.readLine()) != null) { - sb.append(line + "\n"); - } - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - in.close(); - } catch (IOException e) { - LogUtil.error(getClass().getName(), e, ""); - } - } - - return sb.toString(); - } catch (Exception e) { - LogUtil.error(EnhancedJsonTool.class.getName(), e, ""); - } - return ""; - } - protected Object executeScript(String script, Map properties) { Object result = null; try { diff --git a/src/main/java/org/joget/marketplace/auth/AccessTokenProvider.java b/src/main/java/org/joget/marketplace/auth/AccessTokenProvider.java new file mode 100644 index 0000000..c36cd91 --- /dev/null +++ b/src/main/java/org/joget/marketplace/auth/AccessTokenProvider.java @@ -0,0 +1,7 @@ +package org.joget.marketplace.auth; + +import java.util.Map; + +public interface AccessTokenProvider { + String getAccessToken(Map properties); +} diff --git a/src/main/java/org/joget/marketplace/auth/TokenApiAccessTokenProvider.java b/src/main/java/org/joget/marketplace/auth/TokenApiAccessTokenProvider.java new file mode 100644 index 0000000..07b0dc8 --- /dev/null +++ b/src/main/java/org/joget/marketplace/auth/TokenApiAccessTokenProvider.java @@ -0,0 +1,16 @@ +package org.joget.marketplace.auth; + +import org.joget.marketplace.TokenApiUtil; + +import java.util.Map; + +public class TokenApiAccessTokenProvider implements AccessTokenProvider { + @Override + public String getAccessToken(Map properties) { + // If the "accessToken" property is true, retrieve it using TokenApiUtil + if ("true".equalsIgnoreCase((String) properties.get("accessToken"))) { + return new TokenApiUtil().getToken(properties); + } + return ""; + } +} diff --git a/src/main/java/org/joget/marketplace/config/TimeoutConfig.java b/src/main/java/org/joget/marketplace/config/TimeoutConfig.java new file mode 100644 index 0000000..d364558 --- /dev/null +++ b/src/main/java/org/joget/marketplace/config/TimeoutConfig.java @@ -0,0 +1,37 @@ +package org.joget.marketplace.config; + +import org.apache.http.client.config.RequestConfig; +import org.joget.commons.util.LogUtil; + +public class TimeoutConfig { + private final int connectionTimeout; + private final int socketTimeout; + + public TimeoutConfig(String connectionTimeoutStr, String socketTimeoutStr) { + int defaultTimeout = 30000; + int connTimeout = defaultTimeout; + int sockTimeout = defaultTimeout; + try { + if (connectionTimeoutStr != null && !connectionTimeoutStr.isEmpty()) { + connTimeout = Integer.parseInt(connectionTimeoutStr) * 1000; + } + if (socketTimeoutStr != null && !socketTimeoutStr.isEmpty()) { + sockTimeout = Integer.parseInt(socketTimeoutStr) * 1000; + } + } catch (NumberFormatException e) { + LogUtil.warn(getClass().getName(), "Invalid timeout value provided. Using default timeouts."); + } + this.connectionTimeout = connTimeout; + this.socketTimeout = sockTimeout; + LogUtil.info(getClass().getName(), "Connection Timeout set to: " + connectionTimeout + " ms"); + LogUtil.info(getClass().getName(), "Socket Timeout set to: " + socketTimeout + " ms"); + } + + public RequestConfig toRequestConfig() { + return RequestConfig.custom() + .setConnectTimeout(connectionTimeout) + .setSocketTimeout(socketTimeout) + .build(); + } +} + diff --git a/src/main/java/org/joget/marketplace/http/HttpClientFactory.java b/src/main/java/org/joget/marketplace/http/HttpClientFactory.java new file mode 100644 index 0000000..cc8a79a --- /dev/null +++ b/src/main/java/org/joget/marketplace/http/HttpClientFactory.java @@ -0,0 +1,18 @@ +package org.joget.marketplace.http; + +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.SystemDefaultRoutePlanner; +import org.joget.marketplace.config.TimeoutConfig; + +import java.net.ProxySelector; + +public class HttpClientFactory { + public CloseableHttpClient createHttpClient(TimeoutConfig timeoutConfig) { + return HttpClients.custom() + .setDefaultRequestConfig(timeoutConfig.toRequestConfig()) + .setRoutePlanner(new SystemDefaultRoutePlanner(ProxySelector.getDefault())) + .build(); + } +} + diff --git a/src/main/java/org/joget/marketplace/http/HttpClientHandler.java b/src/main/java/org/joget/marketplace/http/HttpClientHandler.java new file mode 100644 index 0000000..7319008 --- /dev/null +++ b/src/main/java/org/joget/marketplace/http/HttpClientHandler.java @@ -0,0 +1,37 @@ +package org.joget.marketplace.http; + +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.SystemDefaultRoutePlanner; +import org.apache.http.HttpResponse; + +import java.io.IOException; +import java.net.ProxySelector; + +public class HttpClientHandler { + private final int connectionTimeout; + private final int socketTimeout; + + public HttpClientHandler(int connectionTimeout, int socketTimeout) { + this.connectionTimeout = connectionTimeout; + this.socketTimeout = socketTimeout; + } + + public CloseableHttpClient createClient() { + RequestConfig config = RequestConfig.custom() + .setConnectTimeout(connectionTimeout) + .setSocketTimeout(socketTimeout) + .build(); + + return HttpClients.custom() + .setDefaultRequestConfig(config) + .setRoutePlanner(new SystemDefaultRoutePlanner(ProxySelector.getDefault())) + .build(); + } + + public HttpResponse executeRequest(CloseableHttpClient client, HttpRequestBase request) throws IOException { + return client.execute(request); + } +} diff --git a/src/main/java/org/joget/marketplace/request/DefaultHttpRequestBuilder.java b/src/main/java/org/joget/marketplace/request/DefaultHttpRequestBuilder.java new file mode 100644 index 0000000..3f0f4c0 --- /dev/null +++ b/src/main/java/org/joget/marketplace/request/DefaultHttpRequestBuilder.java @@ -0,0 +1,96 @@ +package org.joget.marketplace.request; + +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.*; +import org.apache.http.entity.StringEntity; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.joget.commons.util.LogUtil; +import org.joget.workflow.model.WorkflowAssignment; +import org.joget.workflow.util.WorkflowUtil; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.Map; + +public class DefaultHttpRequestBuilder implements HttpRequestBuilder { + @Override + public HttpRequestBase buildRequest(String jsonUrl, Map properties, WorkflowAssignment wfAssignment, String accessToken) throws Exception { + String requestType = getPropertyString(properties, "requestType"); + HttpRequestBase request; + if ("post".equalsIgnoreCase(requestType)) { + request = buildPostRequest(jsonUrl, properties, wfAssignment); + } else if ("put".equalsIgnoreCase(requestType)) { + request = buildPutRequest(jsonUrl, properties, wfAssignment); + } else if ("patch".equalsIgnoreCase(requestType)) { + request = buildPatchRequest(jsonUrl, properties, wfAssignment); + } else { + request = new HttpGet(jsonUrl); + } + addHeaders(request, properties, accessToken); + return request; + } + + private HttpPost buildPostRequest(String url, Map properties, WorkflowAssignment wfAssignment) throws Exception { + HttpPost post = new HttpPost(url); + String postMethod = getPropertyString(properties, "postMethod"); + if ("jsonPayload".equalsIgnoreCase(postMethod)) { + JSONObject obj = new JSONObject(); + Object[] paramsValues = (Object[]) properties.get("params"); + for (Object o : paramsValues) { + Map mapping = (HashMap) o; + String name = mapping.get("name").toString(); + String value = WorkflowUtil.processVariable(mapping.get("value").toString(), "", wfAssignment); + obj.accumulate(name, value); + } + StringEntity requestEntity = new StringEntity(obj.toString(4), "UTF-8"); + post.setEntity(requestEntity); + post.setHeader("Content-type", "application/json"); + LogUtil.info(getClass().getName(), "JSON Payload : " + obj.toString(4)); + } else if ("custom".equalsIgnoreCase(postMethod)) { + String customPayload = getPropertyString(properties, "customPayload"); + StringEntity requestEntity = new StringEntity(customPayload, "UTF-8"); + post.setEntity(requestEntity); + post.setHeader("Content-type", "application/json"); + LogUtil.info(getClass().getName(), "Custom JSON Payload : " + customPayload); + } else { + // Build multipart payload (parameters and attachments) + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + // (Add parameters and file attachments in dedicated helper methods.) + HttpEntity entity = builder.build(); + post.setEntity(entity); + } + return post; + } + + private HttpPut buildPutRequest(String url, Map properties, WorkflowAssignment wfAssignment) throws Exception { + HttpPut put = new HttpPut(url); + // Similar logic as buildPostRequest… + return put; + } + + private HttpPatch buildPatchRequest(String url, Map properties, WorkflowAssignment wfAssignment) throws Exception { + HttpPatch patch = new HttpPatch(url); + // Similar logic as buildPostRequest… + return patch; + } + + private void addHeaders(HttpRequestBase request, Map properties, String accessToken) { + Object[] headerValues = (Object[]) properties.get("headers"); + for (Object o : headerValues) { + Map mapping = (HashMap) o; + String name = mapping.get("name").toString(); + String value = mapping.get("value").toString(); + if (value.contains("{accessToken}")) { + value = value.replace("{accessToken}", accessToken); + } + request.setHeader(name, value); + LogUtil.info(getClass().getName(), "Adding request header " + name + " : " + value); + } + } + + private String getPropertyString(Map properties, String key) { + Object value = properties.get(key); + return value != null ? value.toString() : ""; + } +} + diff --git a/src/main/java/org/joget/marketplace/request/HttpRequestBuilder.java b/src/main/java/org/joget/marketplace/request/HttpRequestBuilder.java new file mode 100644 index 0000000..e8de95c --- /dev/null +++ b/src/main/java/org/joget/marketplace/request/HttpRequestBuilder.java @@ -0,0 +1,11 @@ +package org.joget.marketplace.request; + +import org.apache.http.client.methods.HttpRequestBase; +import org.joget.workflow.model.WorkflowAssignment; + +import java.util.Map; + +public interface HttpRequestBuilder { + HttpRequestBase buildRequest(String jsonUrl, Map properties, WorkflowAssignment wfAssignment, String accessToken) throws Exception; +} + diff --git a/src/main/java/org/joget/marketplace/response/DefaultHttpResponseHandler.java b/src/main/java/org/joget/marketplace/response/DefaultHttpResponseHandler.java new file mode 100644 index 0000000..bc9113e --- /dev/null +++ b/src/main/java/org/joget/marketplace/response/DefaultHttpResponseHandler.java @@ -0,0 +1,65 @@ +package org.joget.marketplace.response; + +import org.apache.http.HttpResponse; +import org.apache.http.util.EntityUtils; +import org.joget.commons.util.LogUtil; +import org.joget.plugin.property.service.PropertyUtil; +import org.joget.workflow.model.WorkflowAssignment; +import org.json.JSONObject; + +import java.util.Map; + +public class DefaultHttpResponseHandler implements HttpResponseHandler { + @Override + public Object handleResponse(HttpResponse response, Map properties, WorkflowAssignment wfAssignment) throws Exception { + String responseType = getPropertyString(properties, "responseType"); + String jsonResponse = EntityUtils.toString(response.getEntity(), "UTF-8"); + + if ("JSON".equalsIgnoreCase(responseType)) { + String jsonResponseFormatted = formatJsonResponse(jsonResponse); + LogUtil.info(getClass().getName(), "Formatted JSON Response: " + jsonResponseFormatted); + JSONObject jsonObj = new JSONObject(jsonResponseFormatted); + Map jsonResponseObject = PropertyUtil.getProperties(jsonObj); + + if ("true".equalsIgnoreCase(getPropertyString(properties, "enableFormatResponse"))) { + properties.put("data", jsonResponseObject); + String script = getPropertyString(properties, "script"); + jsonResponseObject = (Map) executeScript(script, properties); + } + // Optionally store to form or workflow variables here + return jsonResponseObject; + } else { + // Handle binary responses (e.g., file download) + handleBinaryResponse(response, properties, wfAssignment); + return null; + } + } + + private String formatJsonResponse(String jsonResponse) { + if (jsonResponse == null || jsonResponse.isEmpty()) { + return "{}"; + } + if (jsonResponse.startsWith("[") && jsonResponse.endsWith("]")) { + return "{ \"response\" : " + jsonResponse + " }"; + } + if (!jsonResponse.startsWith("{") && !jsonResponse.endsWith("}")) { + return "{ \"response\" : " + jsonResponse + " }"; + } + return jsonResponse; + } + + private void handleBinaryResponse(HttpResponse response, Map properties, WorkflowAssignment wfAssignment) { + // Implement binary response handling (extract filename, save file, update form, etc.) + } + + private Object executeScript(String script, Map properties) { + // Execute formatting script logic (e.g., via BeanShell) + return null; + } + + private String getPropertyString(Map properties, String key) { + Object value = properties.get(key); + return value != null ? value.toString() : ""; + } +} + diff --git a/src/main/java/org/joget/marketplace/response/HttpResponseHandler.java b/src/main/java/org/joget/marketplace/response/HttpResponseHandler.java new file mode 100644 index 0000000..6ef94b4 --- /dev/null +++ b/src/main/java/org/joget/marketplace/response/HttpResponseHandler.java @@ -0,0 +1,10 @@ +package org.joget.marketplace.response; + +import org.apache.http.HttpResponse; +import org.joget.workflow.model.WorkflowAssignment; + +import java.util.Map; + +public interface HttpResponseHandler { + Object handleResponse(HttpResponse response, Map properties, WorkflowAssignment wfAssignment) throws Exception; +} diff --git a/src/main/java/org/joget/marketplace/workflow/WorkflowUpdater.java b/src/main/java/org/joget/marketplace/workflow/WorkflowUpdater.java new file mode 100644 index 0000000..d95fd91 --- /dev/null +++ b/src/main/java/org/joget/marketplace/workflow/WorkflowUpdater.java @@ -0,0 +1,26 @@ +package org.joget.marketplace.workflow; + +import org.joget.workflow.model.WorkflowAssignment; +import org.joget.workflow.model.service.WorkflowManager; + +import java.util.Map; + +public class WorkflowUpdater { + private final WorkflowManager workflowManager; + + public WorkflowUpdater(WorkflowManager workflowManager) { + this.workflowManager = workflowManager; + } + + public void updateResponseStatus(WorkflowAssignment wfAssignment, Map properties, int statusCode) { + String wfVar = getPropertyString(properties); + if (!wfVar.isEmpty()) { + workflowManager.activityVariable(wfAssignment.getActivityId(), wfVar, String.valueOf(statusCode)); + } + } + + private String getPropertyString(Map properties) { + Object value = properties.get("responseStatusWorkflowVariable"); + return value != null ? value.toString() : ""; + } +}