0001: /*******************************************************************************
0002: * Licensed to the Apache Software Foundation (ASF) under one
0003: * or more contributor license agreements. See the NOTICE file
0004: * distributed with this work for additional information
0005: * regarding copyright ownership. The ASF licenses this file
0006: * to you under the Apache License, Version 2.0 (the
0007: * "License"); you may not use this file except in compliance
0008: * with the License. You may obtain a copy of the License at
0009: *
0010: * http://www.apache.org/licenses/LICENSE-2.0
0011: *
0012: * Unless required by applicable law or agreed to in writing,
0013: * software distributed under the License is distributed on an
0014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0015: * KIND, either express or implied. See the License for the
0016: * specific language governing permissions and limitations
0017: * under the License.
0018: *******************************************************************************/package org.ofbiz.service;
0019:
0020: import java.util.*;
0021: import java.lang.reflect.Method;
0022: import java.lang.reflect.Field;
0023: import java.io.Serializable;
0024:
0025: import javax.wsdl.*;
0026: import javax.wsdl.extensions.soap.SOAPBinding;
0027: import javax.wsdl.extensions.soap.SOAPBody;
0028: import javax.wsdl.extensions.soap.SOAPOperation;
0029: import javax.wsdl.extensions.soap.SOAPAddress;
0030: import javax.wsdl.factory.WSDLFactory;
0031: import javax.xml.namespace.QName;
0032:
0033: import javolution.util.FastList;
0034: import javolution.util.FastMap;
0035:
0036: import com.ibm.wsdl.extensions.soap.SOAPBindingImpl;
0037: import com.ibm.wsdl.extensions.soap.SOAPBodyImpl;
0038: import com.ibm.wsdl.extensions.soap.SOAPOperationImpl;
0039: import com.ibm.wsdl.extensions.soap.SOAPAddressImpl;
0040:
0041: import org.ofbiz.base.util.Debug;
0042: import org.ofbiz.base.util.GeneralException;
0043: import org.ofbiz.base.util.ObjectType;
0044: import org.ofbiz.base.util.UtilProperties;
0045: import org.ofbiz.base.util.UtilValidate;
0046: import org.ofbiz.base.util.UtilMisc;
0047: import org.ofbiz.base.util.collections.OrderedSet;
0048: import org.ofbiz.service.group.GroupModel;
0049: import org.ofbiz.service.group.GroupServiceModel;
0050: import org.ofbiz.service.group.ServiceGroupReader;
0051:
0052: import org.apache.commons.collections.set.ListOrderedSet;
0053: import org.w3c.dom.Document;
0054:
0055: /**
0056: * Generic Service Model Class
0057: */
0058: public class ModelService extends AbstractMap implements Serializable {
0059:
0060: public static final String module = ModelService.class.getName();
0061:
0062: public static final String XSD = "http://www.w3.org/2001/XMLSchema";
0063: public static final String TNS = "http://www.ofbiz.org/service/";
0064: public static final String OUT_PARAM = "OUT";
0065: public static final String IN_PARAM = "IN";
0066:
0067: public static final String RESPONSE_MESSAGE = "responseMessage";
0068: public static final String RESPOND_SUCCESS = "success";
0069: public static final String RESPOND_ERROR = "error";
0070: public static final String RESPOND_FAIL = "fail";
0071: public static final String ERROR_MESSAGE = "errorMessage";
0072: public static final String ERROR_MESSAGE_LIST = "errorMessageList";
0073: public static final String ERROR_MESSAGE_MAP = "errorMessageMap";
0074: public static final String SUCCESS_MESSAGE = "successMessage";
0075: public static final String SUCCESS_MESSAGE_LIST = "successMessageList";
0076:
0077: public static final String resource = "ServiceErrorUiLabels";
0078:
0079: /** The name of this service */
0080: public String name;
0081:
0082: /** The description of this service */
0083: public String description;
0084:
0085: /** The name of the service engine */
0086: public String engineName;
0087:
0088: /** The namespace of this service */
0089: public String nameSpace;
0090:
0091: /** The package name or location of this service */
0092: public String location;
0093:
0094: /** The method or function to invoke for this service */
0095: public String invoke;
0096:
0097: /** The default Entity to use for auto-attributes */
0098: public String defaultEntityName;
0099:
0100: /** The loader which loaded this definition */
0101: public String fromLoader;
0102:
0103: /** Does this service require authorization */
0104: public boolean auth;
0105:
0106: /** Can this service be exported via RPC, RMI, SOAP, etc */
0107: public boolean export;
0108:
0109: /** Enable verbose debugging when calling this service */
0110: public boolean debug;
0111:
0112: /** Validate the context info for this service */
0113: public boolean validate;
0114:
0115: /** Create a transaction for this service (if one is not already in place...)? */
0116: public boolean useTransaction;
0117:
0118: /** Require a new transaction for this service */
0119: public boolean requireNewTransaction;
0120:
0121: /** Override the default transaction timeout, only works if we start the transaction */
0122: public int transactionTimeout;
0123:
0124: /** Sets the max number of times this service will retry when failed (persisted async only) */
0125: public int maxRetry = -1;
0126:
0127: /** Permission service name */
0128: public String permissionServiceName;
0129:
0130: /** Permission service main-action */
0131: public String permissionMainAction;
0132:
0133: /** Permission service resource-description */
0134: public String permissionResourceDesc;
0135:
0136: /** Set of services this service implements */
0137: public Set implServices = new ListOrderedSet();
0138:
0139: /** Set of override parameters */
0140: public Set overrideParameters = new ListOrderedSet();
0141:
0142: /** List of permission groups for service invocation */
0143: public List permissionGroups = FastList.newInstance();
0144:
0145: /** List of email-notifications for this service */
0146: public List notifications = FastList.newInstance();
0147:
0148: /** Internal Service Group */
0149: public GroupModel internalGroup = null;
0150:
0151: /** Context Information, a Map of parameters used by the service, contains ModelParam objects */
0152: protected Map contextInfo = FastMap.newInstance();
0153:
0154: /** Context Information, a List of parameters used by the service, contains ModelParam objects */
0155: protected List contextParamList = FastList.newInstance();
0156:
0157: /** Flag to say if we have pulled in our addition parameters from our implemented service(s) */
0158: protected boolean inheritedParameters = false;
0159:
0160: public ModelService() {
0161: }
0162:
0163: public ModelService(ModelService model) {
0164: this .name = model.name;
0165: this .description = model.description;
0166: this .engineName = model.engineName;
0167: this .nameSpace = model.nameSpace;
0168: this .location = model.location;
0169: this .invoke = model.invoke;
0170: this .defaultEntityName = model.defaultEntityName;
0171: this .auth = model.auth;
0172: this .export = model.export;
0173: this .validate = model.validate;
0174: this .useTransaction = model.useTransaction || true;
0175: this .requireNewTransaction = model.requireNewTransaction;
0176: this .transactionTimeout = model.transactionTimeout;
0177: this .maxRetry = model.maxRetry;
0178: this .permissionServiceName = model.permissionServiceName;
0179: this .permissionMainAction = model.permissionMainAction;
0180: this .permissionResourceDesc = model.permissionResourceDesc;
0181: this .implServices = model.implServices;
0182: this .overrideParameters = model.overrideParameters;
0183: this .inheritedParameters = model.inheritedParameters();
0184: this .internalGroup = model.internalGroup;
0185:
0186: List modelParamList = model.getModelParamList();
0187: Iterator i = modelParamList.iterator();
0188: while (i.hasNext()) {
0189: this .addParamClone((ModelParam) i.next());
0190: }
0191: }
0192:
0193: public Object get(Object name) {
0194: Field field;
0195: try {
0196: field = this .getClass().getField(name.toString());
0197: } catch (NoSuchFieldException e) {
0198: return null;
0199: }
0200: if (field != null) {
0201: try {
0202: return field.get(this );
0203: } catch (IllegalAccessException e) {
0204: return null;
0205: }
0206: }
0207: return null;
0208: }
0209:
0210: public Set entrySet() {
0211: return null;
0212: }
0213:
0214: public Object put(Object o1, Object o2) {
0215: return null;
0216: }
0217:
0218: public String toString() {
0219: StringBuffer buf = new StringBuffer();
0220: buf.append(name).append("::");
0221: buf.append(description).append("::");
0222: buf.append(engineName).append("::");
0223: buf.append(nameSpace).append("::");
0224: buf.append(location).append("::");
0225: buf.append(invoke).append("::");
0226: buf.append(defaultEntityName).append("::");
0227: buf.append(auth).append("::");
0228: buf.append(export).append("::");
0229: buf.append(validate).append("::");
0230: buf.append(useTransaction).append("::");
0231: buf.append(requireNewTransaction).append("::");
0232: buf.append(transactionTimeout).append("::");
0233: buf.append(implServices).append("::");
0234: buf.append(overrideParameters).append("::");
0235: buf.append(contextInfo).append("::");
0236: buf.append(contextParamList).append("::");
0237: buf.append(inheritedParameters).append("::");
0238: return buf.toString();
0239: }
0240:
0241: public String debugInfo() {
0242: if (debug || Debug.verboseOn()) {
0243: return " [" + this .toString() + "]";
0244: }
0245: return "";
0246: }
0247:
0248: /**
0249: * Test if we have already inherited our interface parameters
0250: * @return boolean
0251: */
0252: public boolean inheritedParameters() {
0253: return this .inheritedParameters;
0254: }
0255:
0256: /**
0257: * Gets the ModelParam by name
0258: * @param name The name of the parameter to get
0259: * @return ModelParam object with the specified name
0260: */
0261: public ModelParam getParam(String name) {
0262: return (ModelParam) contextInfo.get(name);
0263: }
0264:
0265: /**
0266: * Adds a parameter definition to this service; puts on list in order added
0267: * then sorts by order if specified.
0268: */
0269: public void addParam(ModelParam param) {
0270: if (param != null) {
0271: contextInfo.put(param.name, param);
0272: contextParamList.add(param);
0273: }
0274: }
0275:
0276: /* DEJ20060125 This is private but not used locally, so just commenting it out for now... may remove later
0277: private void copyParams(Collection params) {
0278: if (params != null) {
0279: Iterator i = params.iterator();
0280: while (i.hasNext()) {
0281: ModelParam param = (ModelParam) i.next();
0282: addParam(param);
0283: }
0284: }
0285: }
0286: */
0287:
0288: /**
0289: * Adds a clone of a parameter definition to this service
0290: */
0291: public void addParamClone(ModelParam param) {
0292: if (param != null) {
0293: ModelParam newParam = new ModelParam(param);
0294: addParam(newParam);
0295: }
0296: }
0297:
0298: public Set getAllParamNames() {
0299: Set nameList = new OrderedSet();
0300: Iterator i = this .contextParamList.iterator();
0301:
0302: while (i.hasNext()) {
0303: ModelParam p = (ModelParam) i.next();
0304: nameList.add(p.name);
0305: }
0306: return nameList;
0307: }
0308:
0309: public Set getInParamNames() {
0310: Set nameList = new OrderedSet();
0311: Iterator i = this .contextParamList.iterator();
0312:
0313: while (i.hasNext()) {
0314: ModelParam p = (ModelParam) i.next();
0315: // don't include OUT parameters in this list, only IN and INOUT
0316: if ("OUT".equals(p.mode))
0317: continue;
0318: nameList.add(p.name);
0319: }
0320: return nameList;
0321: }
0322:
0323: // only returns number of defined parameters (not internal)
0324: public int getDefinedInCount() {
0325: int count = 0;
0326:
0327: Iterator i = this .contextParamList.iterator();
0328: while (i.hasNext()) {
0329: ModelParam p = (ModelParam) i.next();
0330: // don't include OUT parameters in this list, only IN and INOUT
0331: if ("OUT".equals(p.mode) || p.internal)
0332: continue;
0333: count++;
0334: }
0335:
0336: return count;
0337: }
0338:
0339: public Set getOutParamNames() {
0340: Set nameList = new OrderedSet();
0341: Iterator i = this .contextParamList.iterator();
0342:
0343: while (i.hasNext()) {
0344: ModelParam p = (ModelParam) i.next();
0345: // don't include IN parameters in this list, only OUT and INOUT
0346: if ("IN".equals(p.mode))
0347: continue;
0348: nameList.add(p.name);
0349: }
0350: return nameList;
0351: }
0352:
0353: // only returns number of defined parameters (not internal)
0354: public int getDefinedOutCount() {
0355: int count = 0;
0356:
0357: Iterator i = this .contextParamList.iterator();
0358: while (i.hasNext()) {
0359: ModelParam p = (ModelParam) i.next();
0360: // don't include IN parameters in this list, only OUT and INOUT
0361: if ("IN".equals(p.mode) || p.internal)
0362: continue;
0363: count++;
0364: }
0365:
0366: return count;
0367: }
0368:
0369: public void updateDefaultValues(Map context, String mode) {
0370: List params = this .getModelParamList();
0371: if (params != null) {
0372: Iterator i = params.iterator();
0373: while (i.hasNext()) {
0374: ModelParam param = (ModelParam) i.next();
0375: if ("INOUT".equals(param.mode)
0376: || mode.equals(param.mode)) {
0377: Object defaultValueObj = param.getDefaultValue();
0378: if (defaultValueObj != null
0379: && context.get(param.name) == null) {
0380: context.put(param.name, defaultValueObj);
0381: Debug.logInfo("Set default value ["
0382: + defaultValueObj + "] for parameter ["
0383: + param.name + "]", module);
0384: }
0385: }
0386: }
0387: }
0388: }
0389:
0390: /**
0391: * Validates a Map against the IN or OUT parameter information
0392: * @param test The Map object to test
0393: * @param mode Test either mode IN or mode OUT
0394: */
0395: public void validate(Map test, String mode, Locale locale)
0396: throws ServiceValidationException {
0397: Map requiredInfo = FastMap.newInstance();
0398: Map optionalInfo = FastMap.newInstance();
0399: boolean verboseOn = Debug.verboseOn();
0400:
0401: if (verboseOn)
0402: Debug.logVerbose("[ModelService.validate] : {" + this .name
0403: + "} : Validating context - " + test, module);
0404:
0405: // do not validate results with errors
0406: if (mode.equals(OUT_PARAM) && test != null
0407: && test.containsKey(RESPONSE_MESSAGE)) {
0408: if (RESPOND_ERROR.equals(test.get(RESPONSE_MESSAGE))
0409: || RESPOND_FAIL.equals(test.get(RESPONSE_MESSAGE))) {
0410: if (verboseOn)
0411: Debug
0412: .logVerbose(
0413: "[ModelService.validate] : {"
0414: + this .name
0415: + "} : response was an error, not validating.",
0416: module);
0417: return;
0418: }
0419: }
0420:
0421: // get the info values
0422: Iterator contextParamIter = this .contextParamList.iterator();
0423: while (contextParamIter.hasNext()) {
0424: ModelParam modelParam = (ModelParam) contextParamIter
0425: .next();
0426: // Debug.logInfo("In ModelService.validate preparing parameter [" + modelParam.name + (modelParam.optional?"(optional):":"(required):") + modelParam.mode + "] for service [" + this.name + "]", module);
0427: if ("INOUT".equals(modelParam.mode)
0428: || mode.equals(modelParam.mode)) {
0429: if (modelParam.optional) {
0430: optionalInfo.put(modelParam.name, modelParam.type);
0431: } else {
0432: requiredInfo.put(modelParam.name, modelParam.type);
0433: }
0434: }
0435: }
0436:
0437: // get the test values
0438: Map requiredTest = FastMap.newInstance();
0439: Map optionalTest = FastMap.newInstance();
0440:
0441: if (test == null)
0442: test = FastMap.newInstance();
0443: requiredTest.putAll(test);
0444:
0445: List requiredButNull = FastList.newInstance();
0446: List keyList = FastList.newInstance();
0447: keyList.addAll(requiredTest.keySet());
0448: Iterator t = keyList.iterator();
0449:
0450: while (t.hasNext()) {
0451: Object key = t.next();
0452: Object value = requiredTest.get(key);
0453:
0454: if (!requiredInfo.containsKey(key)) {
0455: requiredTest.remove(key);
0456: optionalTest.put(key, value);
0457: } else if (value == null) {
0458: requiredButNull.add(key);
0459: }
0460: }
0461:
0462: // check for requiredButNull fields and return an error since null values are not allowed for required fields
0463: if (requiredButNull.size() > 0) {
0464: List missingMsg = FastList.newInstance();
0465: Iterator rbni = requiredButNull.iterator();
0466: while (rbni.hasNext()) {
0467: String missingKey = (String) rbni.next();
0468: String message = this .getParam(missingKey)
0469: .getPrimaryFailMessage(locale);
0470: if (message == null) {
0471: String errMsg = UtilProperties
0472: .getMessage(
0473: ServiceUtil.resource,
0474: "ModelService.following_required_parameter_missing",
0475: locale);
0476: message = errMsg + " [" + this .name + "."
0477: + missingKey + "]";
0478: }
0479: missingMsg.add(message);
0480: }
0481: throw new ServiceValidationException(missingMsg, this ,
0482: requiredButNull, null, mode);
0483: }
0484:
0485: if (verboseOn) {
0486: String requiredNames = "";
0487: Iterator requiredIter = requiredInfo.keySet().iterator();
0488: while (requiredIter.hasNext()) {
0489: requiredNames = requiredNames + requiredIter.next();
0490: if (requiredIter.hasNext()) {
0491: requiredNames = requiredNames + ", ";
0492: }
0493: }
0494: Debug.logVerbose(
0495: "[ModelService.validate] : required fields - "
0496: + requiredNames, module);
0497:
0498: Debug.logVerbose(
0499: "[ModelService.validate] : {" + name + "} : ("
0500: + mode + ") Required - "
0501: + requiredTest.size() + " / "
0502: + requiredInfo.size(), module);
0503: Debug.logVerbose(
0504: "[ModelService.validate] : {" + name + "} : ("
0505: + mode + ") Optional - "
0506: + optionalTest.size() + " / "
0507: + optionalInfo.size(), module);
0508: }
0509:
0510: try {
0511: validate(requiredInfo, requiredTest, true, this , mode,
0512: locale);
0513: validate(optionalInfo, optionalTest, false, this , mode,
0514: locale);
0515: } catch (ServiceValidationException e) {
0516: Debug.logError("[ModelService.validate] : {" + name
0517: + "} : (" + mode + ") Required test error: "
0518: + e.toString(), module);
0519: throw e;
0520: }
0521: }
0522:
0523: /**
0524: * Validates a map of name, object types to a map of name, objects
0525: * @param info The map of name, object types
0526: * @param test The map to test its value types.
0527: * @param reverse Test the maps in reverse.
0528: */
0529: public static void validate(Map info, Map test, boolean reverse,
0530: ModelService model, String mode, Locale locale)
0531: throws ServiceValidationException {
0532: if (info == null || test == null) {
0533: throw new ServiceValidationException(
0534: "Cannot validate NULL maps", model);
0535: }
0536:
0537: // * Validate keys first
0538: Set testSet = test.keySet();
0539: Set keySet = info.keySet();
0540:
0541: // Quick check for sizes
0542: if (info.size() == 0 && test.size() == 0)
0543: return;
0544: // This is to see if the test set contains all from the info set (reverse)
0545: if (reverse && !testSet.containsAll(keySet)) {
0546: Set missing = new TreeSet(keySet);
0547:
0548: missing.removeAll(testSet);
0549: List missingMsgs = FastList.newInstance();
0550:
0551: Iterator iter = missing.iterator();
0552: while (iter.hasNext()) {
0553: String key = (String) iter.next();
0554: String msg = model.getParam(key).getPrimaryFailMessage(
0555: locale);
0556: if (msg == null) {
0557: String errMsg = UtilProperties
0558: .getMessage(
0559: ServiceUtil.resource,
0560: "ModelService.following_required_parameter_missing",
0561: locale);
0562: msg = errMsg + " [" + model.name + "." + key + "]";
0563: }
0564: missingMsgs.add(msg);
0565: }
0566:
0567: List missingCopy = FastList.newInstance();
0568: missingCopy.addAll(missing);
0569: throw new ServiceValidationException(missingMsgs, model,
0570: missingCopy, null, mode);
0571: }
0572:
0573: // This is to see if the info set contains all from the test set
0574: if (!keySet.containsAll(testSet)) {
0575: Set extra = new TreeSet(testSet);
0576:
0577: extra.removeAll(keySet);
0578: List extraMsgs = FastList.newInstance();
0579:
0580: Iterator iter = extra.iterator();
0581: while (iter.hasNext()) {
0582: String key = (String) iter.next();
0583: ModelParam param = model.getParam(key);
0584: String msg = null;
0585: if (param != null) {
0586: msg = param.getPrimaryFailMessage(locale);
0587: }
0588: if (msg == null) {
0589: msg = "Unknown parameter found: [" + model.name
0590: + "." + key + "]";
0591: }
0592: extraMsgs.add(msg);
0593: }
0594:
0595: List extraCopy = FastList.newInstance();
0596: extraCopy.addAll(extra);
0597: throw new ServiceValidationException(extraMsgs, model,
0598: null, extraCopy, mode);
0599: }
0600:
0601: // * Validate types next
0602: List typeFailMsgs = FastList.newInstance();
0603: Iterator i = testSet.iterator();
0604: while (i.hasNext()) {
0605: String key = (String) i.next();
0606: ModelParam param = model.getParam(key);
0607:
0608: Object testObject = test.get(key);
0609: String infoType = (String) info.get(key);
0610:
0611: if (param.validators != null && param.validators.size() > 0) {
0612: Iterator vali = param.validators.iterator();
0613: while (vali.hasNext()) {
0614: ModelParam.ModelParamValidator val = (ModelParam.ModelParamValidator) vali
0615: .next();
0616: if (UtilValidate.isNotEmpty(val.getMethodName())) {
0617: try {
0618: if (!typeValidate(val, testObject)) {
0619: String msg = val.getFailMessage(locale);
0620: if (msg == null) {
0621: msg = "The following parameter failed validation: ["
0622: + model.name
0623: + "."
0624: + key
0625: + "]";
0626: }
0627: typeFailMsgs.add(msg);
0628: }
0629: } catch (GeneralException e) {
0630: Debug.logError(e, module);
0631: String msg = param
0632: .getPrimaryFailMessage(locale);
0633: if (msg == null) {
0634: msg = "The following parameter failed validation: ["
0635: + model.name + "." + key + "]";
0636: }
0637: typeFailMsgs.add(msg);
0638: }
0639: } else {
0640: if (!ObjectType.instanceOf(testObject,
0641: infoType, null)) {
0642: String msg = val.getFailMessage(locale);
0643: if (msg == null) {
0644: msg = "The following parameter failed validation: ["
0645: + model.name + "." + key + "]";
0646: }
0647: typeFailMsgs.add(msg);
0648: }
0649: }
0650: }
0651: } else {
0652: if (!ObjectType.instanceOf(testObject, infoType, null)) {
0653: String testType = testObject == null ? "null"
0654: : testObject.getClass().getName();
0655: String msg = "Type check failed for field ["
0656: + model.name + "." + key
0657: + "]; expected type is [" + infoType
0658: + "]; actual type is [" + testType + "]";
0659: typeFailMsgs.add(msg);
0660: }
0661: }
0662: }
0663:
0664: if (typeFailMsgs.size() > 0) {
0665: throw new ServiceValidationException(typeFailMsgs, model,
0666: mode);
0667: }
0668: }
0669:
0670: public static boolean typeValidate(
0671: ModelParam.ModelParamValidator vali, Object testValue)
0672: throws GeneralException {
0673: // find the validator class
0674: Class validatorClass = null;
0675: try {
0676: validatorClass = ObjectType.loadClass(vali.getClassName());
0677: } catch (ClassNotFoundException e) {
0678: Debug.logWarning(e, module);
0679: }
0680:
0681: if (validatorClass == null) {
0682: throw new GeneralException(
0683: "Unable to load validation class ["
0684: + vali.getClassName() + "]");
0685: }
0686:
0687: boolean foundObjectParam = true;
0688: Class[] stringParam = new Class[] { String.class };
0689: Class[] objectParam = new Class[] { Object.class };
0690:
0691: Method validatorMethod = null;
0692: try {
0693: // try object type first
0694: validatorMethod = validatorClass.getMethod(vali
0695: .getMethodName(), objectParam);
0696: } catch (NoSuchMethodException e) {
0697: foundObjectParam = false;
0698: // next try string type
0699: try {
0700: validatorMethod = validatorClass.getMethod(vali
0701: .getMethodName(), stringParam);
0702: } catch (NoSuchMethodException e2) {
0703: Debug.logWarning(e2, module);
0704: }
0705: }
0706:
0707: if (validatorMethod == null) {
0708: throw new GeneralException(
0709: "Unable to find validation method ["
0710: + vali.getMethodName() + "] in class ["
0711: + vali.getClassName() + "]");
0712: }
0713:
0714: Object[] params;
0715: if (!foundObjectParam) {
0716: // convert to string
0717: String converted;
0718: try {
0719: converted = (String) ObjectType.simpleTypeConvert(
0720: testValue, "String", null, null);
0721: } catch (GeneralException e) {
0722: throw new GeneralException(
0723: "Unable to convert parameter to String");
0724: }
0725: params = new Object[] { converted };
0726: } else {
0727: // use plain object
0728: params = new Object[] { testValue };
0729: }
0730:
0731: // run the validator
0732: Boolean resultBool;
0733: try {
0734: resultBool = (Boolean) validatorMethod.invoke(null, params);
0735: } catch (ClassCastException e) {
0736: throw new GeneralException("Validation method ["
0737: + vali.getMethodName() + "] in class ["
0738: + vali.getClassName()
0739: + "] did not return expected Boolean");
0740: } catch (Exception e) {
0741: throw new GeneralException(
0742: "Unable to run validation method ["
0743: + vali.getMethodName() + "] in class ["
0744: + vali.getClassName() + "]");
0745: }
0746:
0747: return resultBool.booleanValue();
0748: }
0749:
0750: /**
0751: * Gets the parameter names of the specified mode (IN/OUT/INOUT). The
0752: * parameters will be returned in the order specified in the file.
0753: * Note: IN and OUT will also contains INOUT parameters.
0754: * @param mode The mode (IN/OUT/INOUT)
0755: * @param optional True if to include optional parameters
0756: * @param internal True to include internal parameters
0757: * @return List of parameter names
0758: */
0759: public List getParameterNames(String mode, boolean optional,
0760: boolean internal) {
0761: List names = FastList.newInstance();
0762:
0763: if (!"IN".equals(mode) && !"OUT".equals(mode)
0764: && !"INOUT".equals(mode)) {
0765: return names;
0766: }
0767: if (contextInfo.size() == 0) {
0768: return names;
0769: }
0770: Iterator i = contextParamList.iterator();
0771:
0772: while (i.hasNext()) {
0773: ModelParam param = (ModelParam) i.next();
0774:
0775: if (param.mode.equals("INOUT") || param.mode.equals(mode)) {
0776: if (optional || !param.optional) {
0777: if (internal || !param.internal) {
0778: names.add(param.name);
0779: }
0780: }
0781: }
0782: }
0783: return names;
0784: }
0785:
0786: public List getParameterNames(String mode, boolean optional) {
0787: return this .getParameterNames(mode, optional, true);
0788: }
0789:
0790: /**
0791: * Creates a new Map based from an existing map with just valid parameters.
0792: * Tries to convert parameters to required type.
0793: * @param source The source map
0794: * @param mode The mode which to build the new map
0795: */
0796: public Map makeValid(Map source, String mode) {
0797: return makeValid(source, mode, true, null, null);
0798: }
0799:
0800: /**
0801: * Creates a new Map based from an existing map with just valid parameters.
0802: * Tries to convert parameters to required type.
0803: * @param source The source map
0804: * @param mode The mode which to build the new map
0805: * @param includeInternal When false will exclude internal fields
0806: */
0807: public Map makeValid(Map source, String mode,
0808: boolean includeInternal, List errorMessages) {
0809: return makeValid(source, mode, includeInternal, errorMessages,
0810: null);
0811: }
0812:
0813: /**
0814: * Creates a new Map based from an existing map with just valid parameters.
0815: * Tries to convert parameters to required type.
0816: * @param source The source map
0817: * @param mode The mode which to build the new map
0818: * @param includeInternal When false will exclude internal fields
0819: * @param locale locale to use to do some type conversion
0820: */
0821: public Map makeValid(Map source, String mode,
0822: boolean includeInternal, List errorMessages, Locale locale) {
0823: Map target = new HashMap();
0824:
0825: if (source == null) {
0826: return target;
0827: }
0828: if (!"IN".equals(mode) && !"OUT".equals(mode)
0829: && !"INOUT".equals(mode)) {
0830: return target;
0831: }
0832: if (contextInfo.size() == 0) {
0833: return target;
0834: }
0835: Iterator i = contextParamList.iterator();
0836:
0837: while (i.hasNext()) {
0838: ModelParam param = (ModelParam) i.next();
0839: //boolean internalParam = param.internal;
0840:
0841: if (param.mode.equals("INOUT") || param.mode.equals(mode)) {
0842: Object key = param.name;
0843:
0844: // internal map of strings
0845: if (param.stringMapPrefix != null
0846: && param.stringMapPrefix.length() > 0
0847: && !source.containsKey(key)) {
0848: Map paramMap = this .makePrefixMap(source, param);
0849: if (paramMap != null && paramMap.size() > 0) {
0850: target.put(key, paramMap);
0851: }
0852: // internal list of strings
0853: } else if (param.stringListSuffix != null
0854: && param.stringListSuffix.length() > 0
0855: && !source.containsKey(key)) {
0856: List paramList = this .makeSuffixList(source, param);
0857: if (paramList != null && paramList.size() > 0) {
0858: target.put(key, paramList);
0859: }
0860: // other attributes
0861: } else {
0862: if (source.containsKey(key)) {
0863: if ((param.internal && includeInternal)
0864: || (!param.internal)) {
0865: Object value = source.get(key);
0866:
0867: try {
0868: // no need to fail on type conversion; the validator will catch this
0869: value = ObjectType.simpleTypeConvert(
0870: value, param.type, null,
0871: locale, false);
0872: } catch (GeneralException e) {
0873: String errMsg = "Type conversion of field ["
0874: + key
0875: + "] to type ["
0876: + param.type
0877: + "] failed for value \""
0878: + value + "\": " + e.toString();
0879: Debug.logWarning(
0880: "[ModelService.makeValid] : "
0881: + errMsg, module);
0882: if (errorMessages != null) {
0883: errorMessages.add(errMsg);
0884: }
0885: }
0886: target.put(key, value);
0887: }
0888: }
0889: }
0890: }
0891: }
0892: return target;
0893: }
0894:
0895: private Map makePrefixMap(Map source, ModelParam param) {
0896: Map paramMap = new HashMap();
0897: Set sourceSet = source.keySet();
0898: Iterator i = sourceSet.iterator();
0899: while (i.hasNext()) {
0900: String key = (String) i.next();
0901: if (key.startsWith(param.stringMapPrefix)) {
0902: paramMap.put(key, source.get(key));
0903: }
0904: }
0905: return paramMap;
0906: }
0907:
0908: private List makeSuffixList(Map source, ModelParam param) {
0909: List paramList = FastList.newInstance();
0910: Set sourceSet = source.keySet();
0911: Iterator i = sourceSet.iterator();
0912: while (i.hasNext()) {
0913: String key = (String) i.next();
0914: if (key.endsWith(param.stringListSuffix)) {
0915: paramList.add(source.get(key));
0916: }
0917: }
0918: return paramList;
0919: }
0920:
0921: public boolean containsPermissions() {
0922: return (this .permissionGroups != null && this .permissionGroups
0923: .size() > 0);
0924: }
0925:
0926: /**
0927: * Evaluates permission-service for this service.
0928: * @param dctx DispatchContext from the invoked service
0929: * @param context Map containing userLogin and context infromation
0930: * @return result of permission service invocation
0931: */
0932: public Map evalPermission(DispatchContext dctx, Map context) {
0933: if (UtilValidate.isNotEmpty(this .permissionServiceName)) {
0934: ModelService this Service;
0935: ModelService permission;
0936: try {
0937: this Service = dctx.getModelService(this .name);
0938: permission = dctx
0939: .getModelService(this .permissionServiceName);
0940: } catch (GenericServiceException e) {
0941: Debug.logError(e, "Failed to get ModelService: "
0942: + e.toString(), module);
0943: Map result = ServiceUtil.returnSuccess();
0944: result.put("hasPermission", Boolean.FALSE);
0945: result.put("failMessage", e.getMessage());
0946: return result;
0947: }
0948: if (permission != null) {
0949: Map ctx = permission.makeValid(context,
0950: ModelService.IN_PARAM);
0951: if (UtilValidate.isNotEmpty(this .permissionMainAction)) {
0952: ctx.put("mainAction", this .permissionMainAction);
0953: }
0954: if (UtilValidate
0955: .isNotEmpty(this .permissionResourceDesc)) {
0956: ctx.put("resourceDescription",
0957: this .permissionResourceDesc);
0958: } else if (this Service != null) {
0959: ctx.put("resourceDescription", this Service.name);
0960: }
0961:
0962: LocalDispatcher dispatcher = dctx.getDispatcher();
0963: Map resp;
0964: try {
0965: resp = dispatcher.runSync(permission.name, ctx,
0966: 300, true);
0967: } catch (GenericServiceException e) {
0968: Debug.logError(e, module);
0969: Map result = ServiceUtil.returnSuccess();
0970: result.put("hasPermission", Boolean.FALSE);
0971: result.put("failMessage", e.getMessage());
0972: return result;
0973: }
0974: if (ServiceUtil.isError(resp)
0975: || ServiceUtil.isFailure(resp)) {
0976: Map result = ServiceUtil.returnSuccess();
0977: result.put("hasPermission", Boolean.FALSE);
0978: result.put("failMessage", ServiceUtil
0979: .getErrorMessage(resp));
0980: return result;
0981: }
0982: return resp;
0983: } else {
0984: Map result = ServiceUtil.returnSuccess();
0985: result.put("hasPermission", Boolean.FALSE);
0986: result.put("failMessage",
0987: "No ModelService found with the name ["
0988: + this .permissionServiceName + "]");
0989: return result;
0990: }
0991: } else {
0992: Map result = ServiceUtil.returnSuccess();
0993: result.put("hasPermission", Boolean.FALSE);
0994: result
0995: .put("failMessage",
0996: "No ModelService found; no service name specified!");
0997: return result;
0998: }
0999: }
1000:
1001: /**
1002: * Evaluates notifications
1003: */
1004: public void evalNotifications(DispatchContext dctx, Map context,
1005: Map result) {
1006: Iterator i = this .notifications.iterator();
1007: while (i.hasNext()) {
1008: ModelNotification notify = (ModelNotification) i.next();
1009: notify.callNotify(dctx, this , context, result);
1010: }
1011: }
1012:
1013: /**
1014: * Evaluates permissions for a service.
1015: * @param dctx DispatchContext from the invoked service
1016: * @param context Map containing userLogin infromation
1017: * @return true if all permissions evaluate true.
1018: */
1019: public boolean evalPermissions(DispatchContext dctx, Map context) {
1020: // old permission checking
1021: if (this .containsPermissions()) {
1022: Iterator i = this .permissionGroups.iterator();
1023: while (i.hasNext()) {
1024: ModelPermGroup group = (ModelPermGroup) i.next();
1025: if (!group.evalPermissions(dctx, context)) {
1026: return false;
1027: }
1028: }
1029: return true;
1030: } else {
1031: return true;
1032: }
1033: }
1034:
1035: /**
1036: * Gets a list of required IN parameters in sequence.
1037: * @return A list of required IN parameters in the order which they were defined.
1038: */
1039: public List getInParameterSequence(Map source) {
1040: List target = FastList.newInstance();
1041: if (source == null) {
1042: return target;
1043: }
1044: if (contextInfo == null || contextInfo.size() == 0) {
1045: return target;
1046: }
1047: Iterator contextParamIter = this .contextParamList.iterator();
1048: while (contextParamIter.hasNext()) {
1049: ModelParam modelParam = (ModelParam) contextParamIter
1050: .next();
1051:
1052: // don't include OUT parameters in this list, only IN and INOUT
1053: if ("OUT".equals(modelParam.mode))
1054: continue;
1055:
1056: Object srcObject = source.get(modelParam.name);
1057: if (srcObject != null) {
1058: target.add(srcObject);
1059: }
1060: }
1061: return target;
1062: }
1063:
1064: /**
1065: * Returns a list of ModelParam objects in the order they were defined when
1066: * the service was created.
1067: */
1068: public List getModelParamList() {
1069: List newList = FastList.newInstance();
1070: newList.addAll(this .contextParamList);
1071: return newList;
1072: }
1073:
1074: /**
1075: * Returns a list of ModelParam objects in the order they were defined when
1076: * the service was created.
1077: */
1078: public List getInModelParamList() {
1079: List inList = FastList.newInstance();
1080: Iterator contactParamIter = this .contextParamList.iterator();
1081: while (contactParamIter.hasNext()) {
1082: ModelParam modelParam = (ModelParam) contactParamIter
1083: .next();
1084:
1085: // don't include OUT parameters in this list, only IN and INOUT
1086: if ("OUT".equals(modelParam.mode))
1087: continue;
1088:
1089: inList.add(modelParam);
1090: }
1091: return inList;
1092: }
1093:
1094: /**
1095: * Run the interface update and inherit all interface parameters
1096: * @param dctx The DispatchContext to use for service lookups
1097: */
1098: public synchronized void interfaceUpdate(DispatchContext dctx)
1099: throws GenericServiceException {
1100: if (!inheritedParameters) {
1101: // services w/ engine 'group' auto-implement the grouped services
1102: if (this .engineName.equals("group")
1103: && implServices.size() == 0) {
1104: GroupModel group = internalGroup;
1105: if (group == null) {
1106: group = ServiceGroupReader
1107: .getGroupModel(this .location);
1108: }
1109: if (group != null) {
1110: List groupedServices = group.getServices();
1111: Iterator i = groupedServices.iterator();
1112: while (i.hasNext()) {
1113: GroupServiceModel sm = (GroupServiceModel) i
1114: .next();
1115: implServices.add(new ModelServiceIface(sm
1116: .getName(), sm.isOptional()));
1117: if (Debug.verboseOn())
1118: Debug.logVerbose("Adding service ["
1119: + sm.getName()
1120: + "] as interface of: ["
1121: + this .name + "]", module);
1122: }
1123: }
1124: }
1125:
1126: // handle interfaces
1127: if (implServices != null && implServices.size() > 0
1128: && dctx != null) {
1129: Iterator implIter = implServices.iterator();
1130: while (implIter.hasNext()) {
1131: ModelServiceIface iface = (ModelServiceIface) implIter
1132: .next();
1133: String serviceName = iface.getService();
1134: boolean optional = iface.isOptional();
1135:
1136: ModelService model = dctx
1137: .getModelService(serviceName);
1138: if (model != null) {
1139: Iterator contextParamIter = model.contextParamList
1140: .iterator();
1141: while (contextParamIter.hasNext()) {
1142: ModelParam newParam = (ModelParam) contextParamIter
1143: .next();
1144: ModelParam existingParam = (ModelParam) this .contextInfo
1145: .get(newParam.name);
1146: if (existingParam != null) {
1147: // if the existing param is not INOUT and the newParam.mode is different from existingParam.mode, make the existing param optional and INOUT
1148: // TODO: this is another case where having different optional/required settings for IN and OUT would be quite valuable...
1149: if (!"INOUT".equals(existingParam.mode)
1150: && !existingParam.mode
1151: .equals(newParam.mode)) {
1152: existingParam.mode = "INOUT";
1153: if (existingParam.optional
1154: || newParam.optional) {
1155: existingParam.optional = true;
1156: }
1157: }
1158: } else {
1159: ModelParam newParamClone = new ModelParam(
1160: newParam);
1161: if (optional) {
1162: // default option is to make this optional, however the service can override and
1163: // force the clone to use the parents setting.
1164: newParamClone.optional = true;
1165: }
1166: this .addParam(newParamClone);
1167: }
1168: }
1169: } else {
1170: Debug.logWarning("Inherited model ["
1171: + serviceName + "] not found for ["
1172: + this .name + "]", module);
1173: }
1174: }
1175: }
1176:
1177: // handle any override parameters
1178: if (overrideParameters != null
1179: && overrideParameters.size() > 0) {
1180: Iterator keySetIter = overrideParameters.iterator();
1181: while (keySetIter.hasNext()) {
1182: ModelParam overrideParam = (ModelParam) keySetIter
1183: .next();
1184: ModelParam existingParam = (ModelParam) contextInfo
1185: .get(overrideParam.name);
1186:
1187: // keep the list clean, remove it then add it back
1188: contextParamList.remove(existingParam);
1189:
1190: if (existingParam != null) {
1191: // now re-write the parameters
1192: if (UtilValidate.isNotEmpty(overrideParam.type)) {
1193: existingParam.type = overrideParam.type;
1194: }
1195: if (UtilValidate.isNotEmpty(overrideParam.mode)) {
1196: existingParam.mode = overrideParam.mode;
1197: }
1198: if (UtilValidate
1199: .isNotEmpty(overrideParam.entityName)) {
1200: existingParam.entityName = overrideParam.entityName;
1201: }
1202: if (UtilValidate
1203: .isNotEmpty(overrideParam.fieldName)) {
1204: existingParam.fieldName = overrideParam.fieldName;
1205: }
1206: if (UtilValidate
1207: .isNotEmpty(overrideParam.formLabel)) {
1208: existingParam.formLabel = overrideParam.formLabel;
1209: }
1210: if (overrideParam.getDefaultValue() != null) {
1211: existingParam
1212: .copyDefaultValue(overrideParam);
1213: }
1214: if (overrideParam.overrideFormDisplay) {
1215: existingParam.formDisplay = overrideParam.formDisplay;
1216: }
1217: if (overrideParam.overrideOptional) {
1218: existingParam.optional = overrideParam.optional;
1219: }
1220: addParam(existingParam);
1221: } else {
1222: Debug.logWarning(
1223: "Override param found but no parameter existing; ignoring: "
1224: + overrideParam.name, module);
1225: }
1226: }
1227: }
1228:
1229: // set the flag so we don't do this again
1230: this .inheritedParameters = true;
1231: }
1232: }
1233:
1234: public Document toWSDL(String locationURI) throws WSDLException {
1235: WSDLFactory factory = WSDLFactory.newInstance();
1236: Definition def = factory.newDefinition();
1237: def.setTargetNamespace(TNS);
1238: def.addNamespace("xsd", XSD);
1239: def.addNamespace("tns", TNS);
1240: def.addNamespace("soap",
1241: "http://schemas.xmlsoap.org/wsdl/soap/");
1242: this .getWSDL(def, locationURI);
1243: return factory.newWSDLWriter().getDocument(def);
1244: }
1245:
1246: public void getWSDL(Definition def, String locationURI)
1247: throws WSDLException {
1248: // set the IN parameters
1249: Input input = def.createInput();
1250: List inParam = this .getParameterNames(IN_PARAM, true, false);
1251: if (inParam != null) {
1252: Message inMessage = def.createMessage();
1253: inMessage.setQName(new QName(TNS, this .name + "Request"));
1254: inMessage.setUndefined(false);
1255: Iterator i = inParam.iterator();
1256: while (i.hasNext()) {
1257: String paramName = (String) i.next();
1258: ModelParam param = this .getParam(paramName);
1259: if (!param.internal) {
1260: inMessage.addPart(param.getWSDLPart(def));
1261: }
1262: }
1263: def.addMessage(inMessage);
1264: input.setMessage(inMessage);
1265: }
1266:
1267: // set the OUT parameters
1268: Output output = def.createOutput();
1269: List outParam = this .getParameterNames(OUT_PARAM, true, false);
1270: if (outParam != null) {
1271: Message outMessage = def.createMessage();
1272: outMessage.setQName(new QName(TNS, this .name + "Response"));
1273: outMessage.setUndefined(false);
1274: Iterator i = outParam.iterator();
1275: while (i.hasNext()) {
1276: String paramName = (String) i.next();
1277: ModelParam param = this .getParam(paramName);
1278: if (!param.internal) {
1279: outMessage.addPart(param.getWSDLPart(def));
1280: }
1281: }
1282: def.addMessage(outMessage);
1283: output.setMessage(outMessage);
1284: }
1285:
1286: // set port type
1287: Operation operation = def.createOperation();
1288: operation.setName(this .name);
1289: operation.setUndefined(false);
1290: operation.setOutput(output);
1291: operation.setInput(input);
1292:
1293: PortType portType = def.createPortType();
1294: portType.setQName(new QName(TNS, this .name + "PortType"));
1295: portType.addOperation(operation);
1296: portType.setUndefined(false);
1297: def.addPortType(portType);
1298:
1299: // SOAP binding
1300: SOAPBinding soapBinding = new SOAPBindingImpl();
1301: soapBinding.setStyle("document");
1302: soapBinding
1303: .setTransportURI("http://schemas.xmlsoap.org/soap/http");
1304:
1305: Binding binding = def.createBinding();
1306: binding.setQName(new QName(TNS, this .name + "SoapBinding"));
1307: binding.setPortType(portType);
1308: binding.setUndefined(false);
1309: binding.addExtensibilityElement(soapBinding);
1310:
1311: BindingOperation bindingOperation = def
1312: .createBindingOperation();
1313: bindingOperation.setName(operation.getName());
1314: bindingOperation.setOperation(operation);
1315:
1316: SOAPBody soapBody = new SOAPBodyImpl();
1317: soapBody.setUse("literal");
1318: soapBody.setNamespaceURI(TNS);
1319: soapBody.setEncodingStyles(UtilMisc
1320: .toList("http://schemas.xmlsoap.org/soap/encoding/"));
1321:
1322: BindingOutput bindingOutput = def.createBindingOutput();
1323: bindingOutput.addExtensibilityElement(soapBody);
1324: bindingOperation.setBindingOutput(bindingOutput);
1325:
1326: BindingInput bindingInput = def.createBindingInput();
1327: bindingInput.addExtensibilityElement(soapBody);
1328: bindingOperation.setBindingInput(bindingInput);
1329:
1330: SOAPOperation soapOperation = new SOAPOperationImpl();
1331: soapOperation.setSoapActionURI(""); // ?
1332: bindingOperation.addExtensibilityElement(soapOperation);
1333:
1334: binding.addBindingOperation(bindingOperation);
1335: def.addBinding(binding);
1336:
1337: // Service port
1338: Port port = def.createPort();
1339: port.setBinding(binding);
1340: port.setName(this .name + "Port");
1341:
1342: if (locationURI != null) {
1343: SOAPAddress soapAddress = new SOAPAddressImpl();
1344: soapAddress.setLocationURI(locationURI);
1345: port.addExtensibilityElement(soapAddress);
1346: }
1347:
1348: Service service = def.createService();
1349: service.setQName(new QName(TNS, this.name));
1350: service.addPort(port);
1351: def.addService(service);
1352: }
1353: }
|