| /******************************************************************************* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.ofbiz.minilang.method.callops; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| |
| import org.apache.ofbiz.base.util.Debug; |
| import org.apache.ofbiz.base.util.UtilGenerics; |
| import org.apache.ofbiz.base.util.UtilMisc; |
| import org.apache.ofbiz.base.util.UtilProperties; |
| import org.apache.ofbiz.base.util.UtilValidate; |
| import org.apache.ofbiz.base.util.UtilXml; |
| import org.apache.ofbiz.base.util.collections.FlexibleMapAccessor; |
| import org.apache.ofbiz.base.util.collections.FlexibleServletAccessor; |
| import org.apache.ofbiz.base.util.string.FlexibleStringExpander; |
| import org.apache.ofbiz.entity.GenericValue; |
| import org.apache.ofbiz.minilang.MiniLangException; |
| import org.apache.ofbiz.minilang.MiniLangValidate; |
| import org.apache.ofbiz.minilang.SimpleMethod; |
| import org.apache.ofbiz.minilang.artifact.ArtifactInfoContext; |
| import org.apache.ofbiz.minilang.method.MethodContext; |
| import org.apache.ofbiz.minilang.method.MethodOperation; |
| import org.apache.ofbiz.service.GenericServiceException; |
| import org.apache.ofbiz.service.ModelService; |
| import org.apache.ofbiz.service.ServiceUtil; |
| import org.w3c.dom.Element; |
| |
| /** |
| * Implements the <call-service> element. |
| * |
| * @see <a href="https://cwiki.apache.org/confluence/display/OFBADMIN/Mini+Language+-+minilang+-+simple-method+-+Reference">Mini-language Reference</a> |
| */ |
| public final class CallService extends MethodOperation { |
| |
| public static final String module = CallService.class.getName(); |
| public static final String resource = "MiniLangErrorUiLabels"; |
| |
| private final boolean breakOnError; |
| private final FlexibleMessage defaultMessage; |
| private final String errorCode; |
| private final FlexibleMessage errorPrefix; |
| private final FlexibleMessage errorSuffix; |
| private final boolean includeUserLogin; |
| private final FlexibleMapAccessor<Map<String, Object>> inMapFma; |
| private final FlexibleMessage messagePrefix; |
| private final FlexibleMessage messageSuffix; |
| private final boolean requireNewTransaction; |
| private final List<String> resultsToMapList; |
| private final List<ResultToField> resultToFieldList; |
| private final List<ResultToRequest> resultToRequestList; |
| private final List<ResultToResult> resultToResultList; |
| private final List<ResultToSession> resultToSessionList; |
| private final FlexibleStringExpander serviceNameFse; |
| private final String successCode; |
| private final FlexibleMessage successPrefix; |
| private final FlexibleMessage successSuffix; |
| private final int transactionTimeout; |
| |
| public CallService(Element element, SimpleMethod simpleMethod) throws MiniLangException { |
| super(element, simpleMethod); |
| if (MiniLangValidate.validationOn()) { |
| MiniLangValidate.attributeNames(simpleMethod, element, "service-name", "in-map-name", "include-user-login", "break-on-error", "error-code", "require-new-transaction", "transaction-timeout", "success-code"); |
| MiniLangValidate.constantAttributes(simpleMethod, element, "include-user-login", "break-on-error", "error-code", "require-new-transaction", "transaction-timeout", "success-code"); |
| MiniLangValidate.expressionAttributes(simpleMethod, element, "service-name", "in-map-name"); |
| MiniLangValidate.requiredAttributes(simpleMethod, element, "service-name"); |
| MiniLangValidate.childElements(simpleMethod, element, "error-prefix", "error-suffix", "success-prefix", "success-suffix", "message-prefix", "message-suffix", "default-message", "results-to-map", "result-to-field", "result-to-request", "result-to-session", "result-to-result"); |
| } |
| serviceNameFse = FlexibleStringExpander.getInstance(element.getAttribute("service-name")); |
| inMapFma = FlexibleMapAccessor.getInstance(element.getAttribute("in-map-name")); |
| includeUserLogin = !"false".equals(element.getAttribute("include-user-login")); |
| breakOnError = !"false".equals(element.getAttribute("break-on-error")); |
| errorCode = element.getAttribute("error-code"); |
| requireNewTransaction = "true".equals(element.getAttribute("require-new-transaction")); |
| String timeoutStr = UtilXml.checkEmpty(element.getAttribute("transaction-timeout")); |
| int timeout = -1; |
| if (!timeoutStr.isEmpty()) { |
| try { |
| timeout = Integer.parseInt(timeoutStr); |
| } catch (NumberFormatException e) { |
| MiniLangValidate.handleError("Exception thrown while parsing transaction-timeout attribute: " + e.getMessage(), simpleMethod, element); |
| timeout = 0; |
| } |
| } |
| transactionTimeout = timeout; |
| successCode = element.getAttribute("success-code"); |
| errorPrefix = new FlexibleMessage(UtilXml.firstChildElement(element, "error-prefix"), "service.error.prefix"); |
| errorSuffix = new FlexibleMessage(UtilXml.firstChildElement(element, "error-suffix"), "service.error.suffix"); |
| successPrefix = new FlexibleMessage(UtilXml.firstChildElement(element, "success-prefix"), "service.success.prefix"); |
| successSuffix = new FlexibleMessage(UtilXml.firstChildElement(element, "success-suffix"), "service.success.suffix"); |
| messagePrefix = new FlexibleMessage(UtilXml.firstChildElement(element, "message-prefix"), "service.message.prefix"); |
| messageSuffix = new FlexibleMessage(UtilXml.firstChildElement(element, "message-suffix"), "service.message.suffix"); |
| defaultMessage = new FlexibleMessage(UtilXml.firstChildElement(element, "default-message"), null);// "service.default.message" |
| List<? extends Element> resultsToMapElements = UtilXml.childElementList(element, "results-to-map"); |
| if (UtilValidate.isNotEmpty(resultsToMapElements)) { |
| List<String> resultsToMapList = new ArrayList<String>(resultsToMapElements.size()); |
| for (Element resultsToMapElement : resultsToMapElements) { |
| resultsToMapList.add(resultsToMapElement.getAttribute("map-name")); |
| } |
| this.resultsToMapList = Collections.unmodifiableList(resultsToMapList); |
| } else { |
| this.resultsToMapList = null; |
| } |
| List<? extends Element> resultToFieldElements = UtilXml.childElementList(element, "result-to-field"); |
| if (UtilValidate.isNotEmpty(resultToFieldElements)) { |
| List<ResultToField> resultToFieldList = new ArrayList<ResultToField>(resultToFieldElements.size()); |
| for (Element resultToFieldElement : resultToFieldElements) { |
| resultToFieldList.add(new ResultToField(resultToFieldElement)); |
| } |
| this.resultToFieldList = Collections.unmodifiableList(resultToFieldList); |
| } else { |
| this.resultToFieldList = null; |
| } |
| List<? extends Element> resultToRequestElements = UtilXml.childElementList(element, "result-to-request"); |
| if (UtilValidate.isNotEmpty(resultToRequestElements)) { |
| List<ResultToRequest> resultToRequestList = new ArrayList<ResultToRequest>(resultToRequestElements.size()); |
| for (Element resultToRequestElement : resultToRequestElements) { |
| resultToRequestList.add(new ResultToRequest(resultToRequestElement)); |
| } |
| this.resultToRequestList = Collections.unmodifiableList(resultToRequestList); |
| } else { |
| this.resultToRequestList = null; |
| } |
| List<? extends Element> resultToSessionElements = UtilXml.childElementList(element, "result-to-session"); |
| if (UtilValidate.isNotEmpty(resultToSessionElements)) { |
| List<ResultToSession> resultToSessionList = new ArrayList<ResultToSession>(resultToSessionElements.size()); |
| for (Element resultToSessionElement : resultToSessionElements) { |
| resultToSessionList.add(new ResultToSession(resultToSessionElement)); |
| } |
| this.resultToSessionList = Collections.unmodifiableList(resultToSessionList); |
| } else { |
| this.resultToSessionList = null; |
| } |
| List<? extends Element> resultToResultElements = UtilXml.childElementList(element, "result-to-result"); |
| if (UtilValidate.isNotEmpty(resultToResultElements)) { |
| List<ResultToResult> resultToResultList = new ArrayList<ResultToResult>(resultToResultElements.size()); |
| for (Element resultToResultElement : resultToResultElements) { |
| resultToResultList.add(new ResultToResult(resultToResultElement)); |
| } |
| this.resultToResultList = Collections.unmodifiableList(resultToResultList); |
| } else { |
| this.resultToResultList = null; |
| } |
| } |
| |
| @Override |
| public boolean exec(MethodContext methodContext) throws MiniLangException { |
| if (methodContext.isTraceOn()) { |
| outputTraceMessage(methodContext, "Begin call-service."); |
| } |
| String serviceName = serviceNameFse.expandString(methodContext.getEnvMap()); |
| String errorCode = this.errorCode; |
| if (errorCode.isEmpty()) { |
| errorCode = simpleMethod.getDefaultErrorCode(); |
| } |
| String successCode = this.successCode; |
| if (successCode.isEmpty()) { |
| successCode = simpleMethod.getDefaultSuccessCode(); |
| } |
| Map<String, Object> inMap = inMapFma.get(methodContext.getEnvMap()); |
| if (inMap == null) { |
| inMap = new HashMap<String, Object>(); |
| } |
| // before invoking the service, clear messages |
| if (methodContext.getMethodType() == MethodContext.EVENT) { |
| methodContext.removeEnv(simpleMethod.getEventErrorMessageName()); |
| methodContext.removeEnv(simpleMethod.getEventEventMessageName()); |
| methodContext.removeEnv(simpleMethod.getEventResponseCodeName()); |
| } else { |
| methodContext.removeEnv(simpleMethod.getServiceErrorMessageName()); |
| methodContext.removeEnv(simpleMethod.getServiceSuccessMessageName()); |
| methodContext.removeEnv(simpleMethod.getServiceResponseMessageName()); |
| } |
| // add UserLogin to context if expected |
| if (includeUserLogin) { |
| GenericValue userLogin = methodContext.getUserLogin(); |
| if (userLogin != null && inMap.get("userLogin") == null) { |
| inMap.put("userLogin", userLogin); |
| } |
| } |
| // always add Locale to context unless null |
| Locale locale = methodContext.getLocale(); |
| if (locale != null) { |
| inMap.put("locale", locale); |
| } |
| // invoke the service |
| Map<String, Object> result = null; |
| try { |
| ModelService modelService = methodContext.getDispatcher().getDispatchContext().getModelService(serviceName); |
| int timeout = modelService.transactionTimeout; |
| if (this.transactionTimeout >= 0) { |
| timeout = this.transactionTimeout; |
| } |
| if (methodContext.isTraceOn()) { |
| outputTraceMessage(methodContext, "Invoking service \"" + serviceName + "\", require-new-transaction = " + requireNewTransaction + ", transaction-timeout = " + timeout + ", IN attributes:", inMap.toString()); |
| } |
| result = methodContext.getDispatcher().runSync(serviceName, inMap, timeout, requireNewTransaction); |
| } catch (GenericServiceException e) { |
| if (methodContext.isTraceOn()) { |
| outputTraceMessage(methodContext, "Service engine threw an exception: " + e.getMessage()); |
| } |
| String errMsg = "ERROR: Could not complete the " + simpleMethod.getShortDescription() + " process [problem invoking the [" + serviceName + "] service with the map named [" + inMapFma + "] containing [" + inMap + "]: " + e.getMessage() + "]"; |
| Debug.logError(e, errMsg, module); |
| if (breakOnError) { |
| if (methodContext.getMethodType() == MethodContext.EVENT) { |
| methodContext.putEnv(simpleMethod.getEventErrorMessageName(), errMsg); |
| methodContext.putEnv(simpleMethod.getEventResponseCodeName(), errorCode); |
| } else { |
| methodContext.putEnv(simpleMethod.getServiceErrorMessageName(), errMsg); |
| methodContext.putEnv(simpleMethod.getServiceResponseMessageName(), errorCode); |
| } |
| if (methodContext.isTraceOn()) { |
| outputTraceMessage(methodContext, "break-on-error set to \"true\", halting script execution. End call-service."); |
| } |
| return false; |
| } else { |
| if (methodContext.isTraceOn()) { |
| outputTraceMessage(methodContext, "End call-service."); |
| } |
| return true; |
| } |
| } |
| if (resultsToMapList != null) { |
| if (methodContext.isTraceOn()) { |
| outputTraceMessage(methodContext, "Processing " + resultsToMapList.size() + " <results-to-map> elements."); |
| } |
| for (String mapName : resultsToMapList) { |
| methodContext.putEnv(mapName, UtilMisc.makeMapWritable(result)); |
| } |
| } |
| if (resultToFieldList != null) { |
| if (methodContext.isTraceOn()) { |
| outputTraceMessage(methodContext, "Processing " + resultToFieldList.size() + " <result-to-field> elements."); |
| } |
| for (ResultToField rtfDef : resultToFieldList) { |
| rtfDef.exec(methodContext, result); |
| } |
| } |
| if (resultToResultList != null) { |
| if (methodContext.isTraceOn()) { |
| outputTraceMessage(methodContext, "Processing " + resultToResultList.size() + " <result-to-result> elements."); |
| } |
| for (ResultToResult rtrDef : resultToResultList) { |
| rtrDef.exec(methodContext, result); |
| } |
| } |
| if (methodContext.getMethodType() == MethodContext.EVENT) { |
| if (resultToRequestList != null) { |
| if (methodContext.isTraceOn()) { |
| outputTraceMessage(methodContext, "Processing " + resultToRequestList.size() + " <result-to-request> elements."); |
| } |
| for (ResultToRequest rtrDef : resultToRequestList) { |
| rtrDef.exec(methodContext, result); |
| } |
| } |
| if (resultToSessionList != null) { |
| if (methodContext.isTraceOn()) { |
| outputTraceMessage(methodContext, "Processing " + resultToSessionList.size() + " <result-to-session> elements."); |
| } |
| for (ResultToSession rtsDef : resultToSessionList) { |
| rtsDef.exec(methodContext, result); |
| } |
| } |
| } |
| String errorPrefixStr = errorPrefix.getMessage(methodContext.getLoader(), methodContext); |
| String errorSuffixStr = errorSuffix.getMessage(methodContext.getLoader(), methodContext); |
| String successPrefixStr = successPrefix.getMessage(methodContext.getLoader(), methodContext); |
| String successSuffixStr = successSuffix.getMessage(methodContext.getLoader(), methodContext); |
| String messagePrefixStr = messagePrefix.getMessage(methodContext.getLoader(), methodContext); |
| String messageSuffixStr = messageSuffix.getMessage(methodContext.getLoader(), methodContext); |
| String errorMessage = null; |
| List<String> errorMessageList = null; |
| // See if there is a single message |
| if (result.containsKey(ModelService.ERROR_MESSAGE)) { |
| errorMessage = ServiceUtil.makeErrorMessage(result, messagePrefixStr, messageSuffixStr, errorPrefixStr, errorSuffixStr); |
| } else if (result.containsKey(ModelService.ERROR_MESSAGE_LIST)) { |
| errorMessageList = UtilGenerics.checkList(result.get(ModelService.ERROR_MESSAGE_LIST)); |
| } |
| if ((UtilValidate.isNotEmpty(errorMessage) || UtilValidate.isNotEmpty(errorMessageList)) && breakOnError) { |
| if (methodContext.getMethodType() == MethodContext.EVENT) { |
| if (UtilValidate.isNotEmpty(errorMessage)) { |
| if (Debug.verboseOn()) { |
| errorMessage += UtilProperties.getMessage(resource, "simpleMethod.error_show_service_name", UtilMisc.toMap("serviceName", serviceName, "methodName", simpleMethod.getMethodName()), locale); |
| } |
| methodContext.putEnv(simpleMethod.getEventErrorMessageName(), errorMessage); |
| } else { |
| if (Debug.verboseOn()) { |
| errorMessageList.add(UtilProperties.getMessage(resource, "simpleMethod.error_show_service_name", UtilMisc.toMap("serviceName", serviceName, "methodName", simpleMethod.getMethodName()), locale)); |
| } |
| methodContext.putEnv(simpleMethod.getEventErrorMessageListName(), errorMessageList); |
| } |
| } else { |
| ServiceUtil.addErrors(UtilMisc.<String, String> getListFromMap(methodContext.getEnvMap(), this.simpleMethod.getServiceErrorMessageListName()), UtilMisc.<String, String, Object> getMapFromMap(methodContext.getEnvMap(), this.simpleMethod.getServiceErrorMessageMapName()), result); |
| Debug.logError(new Exception(errorMessage), module); |
| } |
| } |
| String successMessage = ServiceUtil.makeSuccessMessage(result, messagePrefixStr, messageSuffixStr, successPrefixStr, successSuffixStr); |
| if (UtilValidate.isNotEmpty(successMessage)) { |
| if (methodContext.getMethodType() == MethodContext.EVENT) { |
| methodContext.putEnv(simpleMethod.getEventEventMessageName(), successMessage); |
| } else { |
| methodContext.putEnv(simpleMethod.getServiceSuccessMessageName(), successMessage); |
| } |
| } |
| String defaultMessageStr = defaultMessage.getMessage(methodContext.getLoader(), methodContext); |
| if (UtilValidate.isEmpty(errorMessage) && UtilValidate.isEmpty(errorMessageList) && UtilValidate.isEmpty(successMessage) && UtilValidate.isNotEmpty(defaultMessageStr)) { |
| if (methodContext.getMethodType() == MethodContext.EVENT) { |
| methodContext.putEnv(simpleMethod.getEventEventMessageName(), defaultMessageStr); |
| } else { |
| methodContext.putEnv(simpleMethod.getServiceSuccessMessageName(), defaultMessageStr); |
| } |
| } |
| String responseCode = result.containsKey(ModelService.RESPONSE_MESSAGE) ? (String) result.get(ModelService.RESPONSE_MESSAGE) : successCode; |
| if (errorCode.equals(responseCode)) { |
| if (methodContext.isTraceOn()) { |
| outputTraceMessage(methodContext, "Service returned an error."); |
| } |
| if (breakOnError) { |
| if (methodContext.getMethodType() == MethodContext.EVENT) { |
| methodContext.putEnv(simpleMethod.getEventResponseCodeName(), responseCode); |
| } else { |
| methodContext.putEnv(simpleMethod.getServiceResponseMessageName(), responseCode); |
| } |
| if (methodContext.isTraceOn()) { |
| outputTraceMessage(methodContext, "break-on-error set to \"true\", halting script execution. End call-service."); |
| } |
| return false; |
| } else { |
| if (methodContext.isTraceOn()) { |
| outputTraceMessage(methodContext, "End call-service."); |
| } |
| return true; |
| } |
| } else { |
| if (methodContext.isTraceOn()) { |
| outputTraceMessage(methodContext, "Service ran successfully. End call-service."); |
| } |
| if (methodContext.getMethodType() == MethodContext.EVENT) { |
| methodContext.putEnv(simpleMethod.getEventResponseCodeName(), responseCode); |
| } else { |
| methodContext.putEnv(simpleMethod.getServiceResponseMessageName(), responseCode); |
| } |
| return true; |
| } |
| } |
| |
| @Override |
| public void gatherArtifactInfo(ArtifactInfoContext aic) { |
| aic.addServiceName(this.serviceNameFse.toString()); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder("<call-service "); |
| sb.append("service-name=\"").append(this.serviceNameFse).append("\" "); |
| if (!this.inMapFma.isEmpty()) { |
| sb.append("in-map-name=\"").append(this.inMapFma).append("\" "); |
| } |
| sb.append("/>"); |
| return sb.toString(); |
| } |
| |
| /** |
| * A factory for the <call-service> element. |
| */ |
| public static final class CallServiceFactory implements Factory<CallService> { |
| @Override |
| public CallService createMethodOperation(Element element, SimpleMethod simpleMethod) throws MiniLangException { |
| return new CallService(element, simpleMethod); |
| } |
| |
| @Override |
| public String getName() { |
| return "call-service"; |
| } |
| } |
| |
| private final class ResultToField { |
| private final FlexibleMapAccessor<Object> fieldFma; |
| private final FlexibleMapAccessor<Object> resultFma; |
| |
| private ResultToField(Element element) { |
| resultFma = FlexibleMapAccessor.getInstance(element.getAttribute("result-name")); |
| String fieldAttribute = element.getAttribute("field"); |
| if (fieldAttribute.isEmpty()) { |
| fieldFma = resultFma; |
| } else { |
| fieldFma = FlexibleMapAccessor.getInstance(fieldAttribute); |
| } |
| } |
| |
| private void exec(MethodContext methodContext, Map<String, Object> resultMap) { |
| fieldFma.put(methodContext.getEnvMap(), resultFma.get(resultMap)); |
| } |
| } |
| |
| private final class ResultToRequest { |
| private final FlexibleMapAccessor<Object> resultFma; |
| private final FlexibleServletAccessor<Object> requestFsa; |
| |
| private ResultToRequest(Element element) { |
| requestFsa = new FlexibleServletAccessor<Object>(element.getAttribute("request-name"), element.getAttribute("result-name")); |
| resultFma =FlexibleMapAccessor.getInstance(element.getAttribute("result-name")); |
| } |
| |
| private void exec(MethodContext methodContext, Map<String, Object> resultMap) { |
| requestFsa.put(methodContext.getRequest(), resultFma.get(resultMap), methodContext.getEnvMap()); |
| } |
| } |
| |
| private final class ResultToResult { |
| private final FlexibleMapAccessor<Object> resultFma; |
| private final FlexibleMapAccessor<Object> serviceResultFma; |
| |
| private ResultToResult(Element element) { |
| resultFma = FlexibleMapAccessor.getInstance(element.getAttribute("result-name")); |
| String serviceResultAttribute = element.getAttribute("service-result-name"); |
| if (serviceResultAttribute.isEmpty()) { |
| serviceResultFma = resultFma; |
| } else { |
| serviceResultFma = FlexibleMapAccessor.getInstance(serviceResultAttribute); |
| } |
| } |
| |
| private void exec(MethodContext methodContext, Map<String, Object> resultMap) { |
| serviceResultFma.put(methodContext.getResults(), resultFma.get(resultMap)); |
| } |
| } |
| |
| private final class ResultToSession { |
| private final FlexibleMapAccessor<Object> resultFma; |
| private final FlexibleServletAccessor<Object> requestFsa; |
| |
| private ResultToSession(Element element) { |
| requestFsa = new FlexibleServletAccessor<Object>(element.getAttribute("session-name"), element.getAttribute("result-name")); |
| resultFma =FlexibleMapAccessor.getInstance(element.getAttribute("result-name")); |
| } |
| |
| private void exec(MethodContext methodContext, Map<String, Object> resultMap) { |
| requestFsa.put(methodContext.getRequest().getSession(), resultFma.get(resultMap), methodContext.getEnvMap()); |
| } |
| } |
| } |