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.Iterator;
0021: import java.util.List;
0022: import java.util.Locale;
0023: import java.util.Map;
0024: import javax.transaction.Transaction;
0025:
0026: import javolution.util.FastList;
0027: import javolution.util.FastMap;
0028: import org.apache.commons.collections.map.LRUMap;
0029: import org.w3c.dom.Element;
0030:
0031: import org.ofbiz.base.config.GenericConfigException;
0032: import org.ofbiz.base.util.Debug;
0033: import org.ofbiz.base.util.UtilMisc;
0034: import org.ofbiz.base.util.UtilTimer;
0035: import org.ofbiz.base.util.UtilValidate;
0036: import org.ofbiz.base.util.UtilXml;
0037: import org.ofbiz.base.util.GeneralRuntimeException;
0038: import org.ofbiz.entity.GenericDelegator;
0039: import org.ofbiz.entity.GenericEntityException;
0040: import org.ofbiz.entity.GenericValue;
0041: import org.ofbiz.entity.transaction.DebugXaResource;
0042: import org.ofbiz.entity.transaction.GenericTransactionException;
0043: import org.ofbiz.entity.transaction.TransactionUtil;
0044: import org.ofbiz.security.Security;
0045: import org.ofbiz.security.SecurityConfigurationException;
0046: import org.ofbiz.security.SecurityFactory;
0047: import org.ofbiz.service.config.ServiceConfigUtil;
0048: import org.ofbiz.service.eca.ServiceEcaUtil;
0049: import org.ofbiz.service.engine.GenericEngine;
0050: import org.ofbiz.service.engine.GenericEngineFactory;
0051: import org.ofbiz.service.group.ServiceGroupReader;
0052: import org.ofbiz.service.jms.JmsListenerFactory;
0053: import org.ofbiz.service.job.JobManager;
0054: import org.ofbiz.service.job.JobManagerException;
0055:
0056: /**
0057: * Global Service Dispatcher
0058: */
0059: public class ServiceDispatcher {
0060:
0061: public static final String module = ServiceDispatcher.class
0062: .getName();
0063: public static final int lruLogSize = 200;
0064:
0065: protected static Map runLog = new LRUMap(lruLogSize);
0066: protected static Map dispatchers = FastMap.newInstance();
0067: protected static boolean enableJM = true;
0068: protected static boolean enableJMS = true;
0069: protected static boolean enableSvcs = true;
0070:
0071: protected GenericDelegator delegator = null;
0072: protected GenericEngineFactory factory = null;
0073: protected Security security = null;
0074: protected Map localContext = null;
0075: protected Map callbacks = null;
0076: protected JobManager jm = null;
0077: protected JmsListenerFactory jlf = null;
0078:
0079: public ServiceDispatcher(GenericDelegator delegator,
0080: boolean enableJM, boolean enableJMS, boolean enableSvcs) {
0081: Debug.logInfo("[ServiceDispatcher] : Creating new instance.",
0082: module);
0083: factory = new GenericEngineFactory(this );
0084: ServiceGroupReader.readConfig();
0085: ServiceEcaUtil.readConfig();
0086:
0087: this .delegator = delegator;
0088: this .localContext = FastMap.newInstance();
0089: this .callbacks = FastMap.newInstance();
0090:
0091: if (delegator != null) {
0092: try {
0093: this .security = SecurityFactory.getInstance(delegator);
0094: } catch (SecurityConfigurationException e) {
0095: Debug
0096: .logError(
0097: e,
0098: "[ServiceDispatcher.init] : No instance of security imeplemtation found.",
0099: module);
0100: }
0101: }
0102:
0103: // make sure we haven't disabled these features from running
0104: if (enableJM) {
0105: try {
0106: this .jm = new JobManager(this .delegator);
0107: } catch (GeneralRuntimeException e) {
0108: Debug.logWarning(e.getMessage(), module);
0109: }
0110: }
0111:
0112: if (enableJMS) {
0113: this .jlf = new JmsListenerFactory(this );
0114: }
0115:
0116: if (enableSvcs) {
0117: this .runStartupServices();
0118: }
0119: }
0120:
0121: public ServiceDispatcher(GenericDelegator delegator) {
0122: this (delegator, enableJM, enableJMS, enableSvcs);
0123: }
0124:
0125: /**
0126: * Returns a pre-registered instance of the ServiceDispatcher associated with this delegator.
0127: * @param delegator the local delegator
0128: * @return A reference to this global ServiceDispatcher
0129: */
0130: public static ServiceDispatcher getInstance(String name,
0131: GenericDelegator delegator) {
0132: ServiceDispatcher sd = getInstance(null, null, delegator);
0133:
0134: if (!sd.containsContext(name)) {
0135: return null;
0136: }
0137: return sd;
0138: }
0139:
0140: /**
0141: * Returns an instance of the ServiceDispatcher associated with this delegator and registers the loader.
0142: * @param name the local dispatcher
0143: * @param context the context of the local dispatcher
0144: * @param delegator the local delegator
0145: * @return A reference to this global ServiceDispatcher
0146: */
0147: public static ServiceDispatcher getInstance(String name,
0148: DispatchContext context, GenericDelegator delegator) {
0149: ServiceDispatcher sd;
0150:
0151: String dispatcherKey = delegator != null ? delegator
0152: .getDelegatorName() : "null";
0153: sd = (ServiceDispatcher) dispatchers.get(dispatcherKey);
0154: if (sd == null) {
0155: synchronized (ServiceDispatcher.class) {
0156: if (Debug.verboseOn())
0157: Debug.logVerbose(
0158: "[ServiceDispatcher.getInstance] : No instance found ("
0159: + dispatcherKey + ").", module);
0160: sd = (ServiceDispatcher) dispatchers.get(dispatcherKey);
0161: if (sd == null) {
0162: sd = new ServiceDispatcher(delegator);
0163: dispatchers.put(dispatcherKey, sd);
0164: }
0165: }
0166: }
0167: if (name != null && context != null) {
0168: sd.register(name, context);
0169: }
0170: return sd;
0171: }
0172:
0173: /**
0174: * Registers the loader with this ServiceDispatcher
0175: * @param name the local dispatcher
0176: * @param context the context of the local dispatcher
0177: */
0178: public void register(String name, DispatchContext context) {
0179: if (Debug.infoOn())
0180: Debug.logInfo(
0181: "Registered dispatcher: " + context.getName(),
0182: module);
0183: this .localContext.put(name, context);
0184: }
0185:
0186: /**
0187: * De-Registers the loader with this ServiceDispatcher
0188: * @param local the LocalDispatcher to de-register
0189: */
0190: public void deregister(LocalDispatcher local) {
0191: if (Debug.infoOn())
0192: Debug.logInfo("De-Registering dispatcher: "
0193: + local.getName(), module);
0194: localContext.remove(local.getName());
0195: if (localContext.size() == 1) { // 1 == the JMSDispatcher
0196: try {
0197: this .shutdown();
0198: } catch (GenericServiceException e) {
0199: Debug.logError(e,
0200: "Trouble shutting down ServiceDispatcher!",
0201: module);
0202: }
0203: }
0204: }
0205:
0206: public synchronized void registerCallback(String serviceName,
0207: GenericServiceCallback cb) {
0208: List callBackList = (List) callbacks.get(serviceName);
0209: if (callBackList == null) {
0210: callBackList = FastList.newInstance();
0211: }
0212: callBackList.add(cb);
0213: callbacks.put(serviceName, callBackList);
0214: }
0215:
0216: public List getCallbacks(String serviceName) {
0217: return (List) callbacks.get(serviceName);
0218: }
0219:
0220: /**
0221: * Run the service synchronously and return the result.
0222: * @param localName Name of the context to use.
0223: * @param service Service model object.
0224: * @param context Map of name, value pairs composing the context.
0225: * @return Map of name, value pairs composing the result.
0226: * @throws ServiceAuthException
0227: * @throws ServiceValidationException
0228: * @throws GenericServiceException
0229: */
0230: public Map runSync(String localName, ModelService service,
0231: Map context) throws ServiceAuthException,
0232: ServiceValidationException, GenericServiceException {
0233: return runSync(localName, service, context, true);
0234: }
0235:
0236: /**
0237: * Run the service synchronously and IGNORE the result.
0238: * @param localName Name of the context to use.
0239: * @param service Service model object.
0240: * @param context Map of name, value pairs composing the context.
0241: * @throws ServiceAuthException
0242: * @throws ServiceValidationException
0243: * @throws GenericServiceException
0244: */
0245: public void runSyncIgnore(String localName, ModelService service,
0246: Map context) throws ServiceAuthException,
0247: ServiceValidationException, GenericServiceException {
0248: runSync(localName, service, context, false);
0249: }
0250:
0251: /**
0252: * Run the service synchronously and return the result.
0253: * @param localName Name of the context to use.
0254: * @param modelService Service model object.
0255: * @param context Map of name, value pairs composing the context.
0256: * @param validateOut Validate OUT parameters
0257: * @return Map of name, value pairs composing the result.
0258: * @throws ServiceAuthException
0259: * @throws ServiceValidationException
0260: * @throws GenericServiceException
0261: */
0262: public Map runSync(String localName, ModelService modelService,
0263: Map context, boolean validateOut)
0264: throws ServiceAuthException, ServiceValidationException,
0265: GenericServiceException {
0266: long serviceStartTime = System.currentTimeMillis();
0267: boolean debugging = checkDebug(modelService, 1, true);
0268: if (Debug.verboseOn()) {
0269: Debug.logVerbose(
0270: "[ServiceDispatcher.runSync] : invoking service "
0271: + modelService.name + " ["
0272: + modelService.location + "/"
0273: + modelService.invoke + "] ("
0274: + modelService.engineName + ")", module);
0275: }
0276:
0277: if (context == null) {
0278: context = FastMap.newInstance();
0279: }
0280:
0281: // setup the result map
0282: Map result = FastMap.newInstance();
0283: boolean isFailure = false;
0284: boolean isError = false;
0285:
0286: // set up the running service log
0287: RunningService rs = this .logService(localName, modelService,
0288: GenericEngine.SYNC_MODE);
0289:
0290: // get eventMap once for all calls for speed, don't do event calls if it is null
0291: Map eventMap = ServiceEcaUtil
0292: .getServiceEventMap(modelService.name);
0293:
0294: // check the locale
0295: Locale locale = this .checkLocale(context);
0296:
0297: // setup the engine and context
0298: DispatchContext ctx = (DispatchContext) localContext
0299: .get(localName);
0300: GenericEngine engine = this
0301: .getGenericEngine(modelService.engineName);
0302:
0303: // setup default IN values
0304: modelService
0305: .updateDefaultValues(context, ModelService.IN_PARAM);
0306:
0307: Map ecaContext = null;
0308:
0309: // for isolated transactions
0310: Transaction parentTransaction = null;
0311:
0312: // start the transaction
0313: boolean beganTrans = false;
0314: try {
0315: if (modelService.useTransaction) {
0316: beganTrans = TransactionUtil
0317: .begin(modelService.transactionTimeout);
0318: // isolate the transaction if defined
0319: if (modelService.requireNewTransaction && !beganTrans) {
0320: parentTransaction = TransactionUtil.suspend();
0321: // now start a new transaction
0322: beganTrans = TransactionUtil
0323: .begin(modelService.transactionTimeout);
0324: }
0325: }
0326:
0327: // XAResource debugging
0328: if (beganTrans && TransactionUtil.debugResources) {
0329: DebugXaResource dxa = new DebugXaResource(
0330: modelService.name);
0331: try {
0332: dxa.enlist();
0333: } catch (Exception e) {
0334: Debug.logError(e, module);
0335: }
0336: }
0337:
0338: try {
0339: // setup global transaction ECA listeners to execute later
0340: if (eventMap != null)
0341: ServiceEcaUtil.evalRules(modelService.name,
0342: eventMap, "global-rollback", ctx, context,
0343: result, isError, isFailure);
0344: if (eventMap != null)
0345: ServiceEcaUtil.evalRules(modelService.name,
0346: eventMap, "global-commit", ctx, context,
0347: result, isError, isFailure);
0348:
0349: // pre-auth ECA
0350: if (eventMap != null)
0351: ServiceEcaUtil.evalRules(modelService.name,
0352: eventMap, "auth", ctx, context, result,
0353: isError, isFailure);
0354:
0355: // check for pre-auth failure/errors
0356: isFailure = ServiceUtil.isFailure(result);
0357: isError = ServiceUtil.isError(result);
0358:
0359: context = checkAuth(localName, context, modelService);
0360: Object userLogin = context.get("userLogin");
0361:
0362: if (modelService.auth && userLogin == null) {
0363: throw new ServiceAuthException(
0364: "User authorization is required for this service: "
0365: + modelService.name
0366: + modelService.debugInfo());
0367: }
0368:
0369: // pre-validate ECA
0370: if (eventMap != null)
0371: ServiceEcaUtil.evalRules(modelService.name,
0372: eventMap, "in-validate", ctx, context,
0373: result, isError, isFailure);
0374:
0375: // check for pre-validate failure/errors
0376: isFailure = ServiceUtil.isFailure(result);
0377: isError = ServiceUtil.isError(result);
0378:
0379: // validate the context
0380: if (modelService.validate && !isError && !isFailure) {
0381: try {
0382: modelService.validate(context,
0383: ModelService.IN_PARAM, locale);
0384: } catch (ServiceValidationException e) {
0385: Debug
0386: .logError(
0387: e,
0388: "Incoming context (in runSync : "
0389: + modelService.name
0390: + ") does not match expected requirements",
0391: module);
0392: throw e;
0393: }
0394: }
0395:
0396: // pre-invoke ECA
0397: if (eventMap != null)
0398: ServiceEcaUtil.evalRules(modelService.name,
0399: eventMap, "invoke", ctx, context, result,
0400: isError, isFailure);
0401:
0402: // check for pre-invoke failure/errors
0403: isFailure = ServiceUtil.isFailure(result);
0404: isError = ServiceUtil.isError(result);
0405:
0406: // ===== invoke the service =====
0407: if (!isError && !isFailure) {
0408: Map invokeResult = engine.runSync(localName,
0409: modelService, context);
0410: engine.sendCallbacks(modelService, context,
0411: invokeResult, GenericEngine.SYNC_MODE);
0412: if (invokeResult != null) {
0413: result.putAll(invokeResult);
0414: } else {
0415: Debug.logWarning("Service (in runSync : "
0416: + modelService.name
0417: + ") returns null result", module);
0418: }
0419: }
0420:
0421: // re-check the errors/failures
0422: isFailure = ServiceUtil.isFailure(result);
0423: isError = ServiceUtil.isError(result);
0424:
0425: // create a new context with the results to pass to ECA services; necessary because caller may reuse this context
0426: ecaContext = FastMap.newInstance();
0427: ecaContext.putAll(context);
0428:
0429: // copy all results: don't worry parameters that aren't allowed won't be passed to the ECA services
0430: ecaContext.putAll(result);
0431:
0432: // setup default OUT values
0433: modelService.updateDefaultValues(context,
0434: ModelService.OUT_PARAM);
0435:
0436: // validate the result
0437: if (modelService.validate && validateOut) {
0438: // pre-out-validate ECA
0439: if (eventMap != null)
0440: ServiceEcaUtil.evalRules(modelService.name,
0441: eventMap, "out-validate", ctx,
0442: ecaContext, result, isError, isFailure);
0443: try {
0444: modelService.validate(result,
0445: ModelService.OUT_PARAM, locale);
0446: } catch (ServiceValidationException e) {
0447: Debug
0448: .logError(
0449: e,
0450: "Outgoing result (in runSync : "
0451: + modelService.name
0452: + ") does not match expected requirements",
0453: module);
0454: throw e;
0455: }
0456: }
0457:
0458: // pre-commit ECA
0459: if (eventMap != null)
0460: ServiceEcaUtil.evalRules(modelService.name,
0461: eventMap, "commit", ctx, ecaContext,
0462: result, isError, isFailure);
0463:
0464: // check for pre-commit failure/errors
0465: isFailure = ServiceUtil.isFailure(result);
0466: isError = ServiceUtil.isError(result);
0467:
0468: // check for failure and log on info level; this is used for debugging
0469: if (isFailure) {
0470: Debug.logWarning("Service Failure ["
0471: + modelService.name + "]: "
0472: + ServiceUtil.getErrorMessage(result),
0473: module);
0474: }
0475:
0476: } catch (Throwable t) {
0477: if (Debug.timingOn()) {
0478: UtilTimer.closeTimer(localName + " / "
0479: + modelService.name,
0480: "Sync service failed...", module);
0481: }
0482: String errMsg = "Service [" + modelService.name
0483: + "] threw an unexpected exception/error";
0484: Debug.logError(t, errMsg, module);
0485: engine.sendCallbacks(modelService, context, t,
0486: GenericEngine.SYNC_MODE);
0487: try {
0488: TransactionUtil.rollback(beganTrans, errMsg, t);
0489: } catch (GenericTransactionException te) {
0490: Debug.logError(te, "Cannot rollback transaction",
0491: module);
0492: }
0493: checkDebug(modelService, 0, debugging);
0494: rs.setEndStamp();
0495: if (t instanceof ServiceAuthException) {
0496: throw (ServiceAuthException) t;
0497: } else if (t instanceof ServiceValidationException) {
0498: throw (ServiceValidationException) t;
0499: } else if (t instanceof GenericServiceException) {
0500: throw (GenericServiceException) t;
0501: } else {
0502: throw new GenericServiceException("Service ["
0503: + modelService.name + "] Failed"
0504: + modelService.debugInfo(), t);
0505: }
0506: } finally {
0507: // if there was an error, rollback transaction, otherwise commit
0508: if (isError) {
0509: String errMsg = "Service Error ["
0510: + modelService.name + "]: "
0511: + ServiceUtil.getErrorMessage(result);
0512: // try to log the error
0513: Debug.logError(errMsg, module);
0514:
0515: // rollback the transaction
0516: try {
0517: TransactionUtil.rollback(beganTrans, errMsg,
0518: null);
0519: } catch (GenericTransactionException e) {
0520: Debug.logError(e,
0521: "Could not rollback transaction: "
0522: + e.toString(), module);
0523: }
0524: } else {
0525: // commit the transaction
0526: try {
0527: TransactionUtil.commit(beganTrans);
0528: } catch (GenericTransactionException e) {
0529: String errMsg = "Could not commit transaction for service ["
0530: + modelService.name + "] call";
0531: Debug.logError(e, errMsg, module);
0532: if (e.getMessage() != null) {
0533: errMsg = errMsg + ": " + e.getMessage();
0534: }
0535: throw new GenericServiceException(errMsg);
0536: }
0537: }
0538:
0539: // call notifications -- event is determined from the result (success, error, fail)
0540: modelService.evalNotifications(this
0541: .getLocalContext(localName), context, result);
0542: }
0543: } catch (GenericTransactionException te) {
0544: Debug.logError(te, "Problems with the transaction", module);
0545: throw new GenericServiceException(
0546: "Problems with the transaction.", te.getNested());
0547: } finally {
0548: // resume the parent transaction
0549: if (parentTransaction != null) {
0550: try {
0551: TransactionUtil.resume(parentTransaction);
0552: } catch (GenericTransactionException ite) {
0553: Debug.logWarning(ite,
0554: "Transaction error, not resumed", module);
0555: throw new GenericServiceException(
0556: "Resume transaction exception, see logs");
0557: }
0558: }
0559: }
0560:
0561: // pre-return ECA
0562: if (eventMap != null)
0563: ServiceEcaUtil.evalRules(modelService.name, eventMap,
0564: "return", ctx, ecaContext, result, isError,
0565: isFailure);
0566:
0567: checkDebug(modelService, 0, debugging);
0568: rs.setEndStamp();
0569:
0570: long timeToRun = System.currentTimeMillis() - serviceStartTime;
0571: if (Debug.timingOn() && timeToRun > 50) {
0572: Debug.logTiming("Sync service [" + localName + "/"
0573: + modelService.name + "] finished in [" + timeToRun
0574: + "] milliseconds", module);
0575: } else if (timeToRun > 200) {
0576: Debug.logInfo("Sync service [" + localName + "/"
0577: + modelService.name + "] finished in [" + timeToRun
0578: + "] milliseconds", module);
0579: }
0580:
0581: return result;
0582: }
0583:
0584: /**
0585: * Run the service asynchronously, passing an instance of GenericRequester that will receive the result.
0586: * @param localName Name of the context to use.
0587: * @param service Service model object.
0588: * @param context Map of name, value pairs composing the context.
0589: * @param requester Object implementing GenericRequester interface which will receive the result.
0590: * @param persist True for store/run; False for run.
0591: * @throws ServiceAuthException
0592: * @throws ServiceValidationException
0593: * @throws GenericServiceException
0594: */
0595: public void runAsync(String localName, ModelService service,
0596: Map context, GenericRequester requester, boolean persist)
0597: throws ServiceAuthException, ServiceValidationException,
0598: GenericServiceException {
0599: if (Debug.timingOn()) {
0600: UtilTimer.timerLog(localName + " / " + service.name,
0601: "ASync service started...", module);
0602: }
0603: boolean debugging = checkDebug(service, 1, true);
0604: if (Debug.verboseOn()) {
0605: Debug.logVerbose(
0606: "[ServiceDispatcher.runAsync] : prepareing service "
0607: + service.name + " [" + service.location
0608: + "/" + service.invoke + "] ("
0609: + service.engineName + ")", module);
0610: }
0611:
0612: if (context == null) {
0613: context = FastMap.newInstance();
0614: }
0615:
0616: // setup the result map
0617: Map result = FastMap.newInstance();
0618: boolean isFailure = false;
0619: boolean isError = false;
0620:
0621: // set up the running service log
0622: this .logService(localName, service, GenericEngine.ASYNC_MODE);
0623:
0624: // check the locale
0625: Locale locale = this .checkLocale(context);
0626:
0627: // setup the engine and context
0628: DispatchContext ctx = (DispatchContext) localContext
0629: .get(localName);
0630: GenericEngine engine = this
0631: .getGenericEngine(service.engineName);
0632:
0633: // for isolated transactions
0634: Transaction parentTransaction = null;
0635: // start the transaction
0636: boolean beganTrans = false;
0637:
0638: try {
0639: if (service.useTransaction) {
0640: beganTrans = TransactionUtil
0641: .begin(service.transactionTimeout);
0642:
0643: // isolate the transaction if defined
0644: if (service.requireNewTransaction && !beganTrans) {
0645: parentTransaction = TransactionUtil.suspend();
0646: // now start a new transaction
0647: beganTrans = TransactionUtil
0648: .begin(service.transactionTimeout);
0649: }
0650: }
0651:
0652: // XAResource debugging
0653: if (beganTrans && TransactionUtil.debugResources) {
0654: DebugXaResource dxa = new DebugXaResource(service.name);
0655: try {
0656: dxa.enlist();
0657: } catch (Exception e) {
0658: Debug.logError(e, module);
0659: }
0660: }
0661:
0662: try {
0663: // get eventMap once for all calls for speed, don't do event calls if it is null
0664: Map eventMap = ServiceEcaUtil
0665: .getServiceEventMap(service.name);
0666:
0667: // pre-auth ECA
0668: if (eventMap != null)
0669: ServiceEcaUtil.evalRules(service.name, eventMap,
0670: "auth", ctx, context, result, isError,
0671: isFailure);
0672:
0673: context = checkAuth(localName, context, service);
0674: Object userLogin = context.get("userLogin");
0675:
0676: if (service.auth && userLogin == null)
0677: throw new ServiceAuthException(
0678: "User authorization is required for this service: "
0679: + service.name
0680: + service.debugInfo());
0681:
0682: // pre-validate ECA
0683: if (eventMap != null)
0684: ServiceEcaUtil.evalRules(service.name, eventMap,
0685: "in-validate", ctx, context, result,
0686: isError, isFailure);
0687:
0688: // check for pre-validate failure/errors
0689: isFailure = ModelService.RESPOND_FAIL.equals(result
0690: .get(ModelService.RESPONSE_MESSAGE));
0691: isError = ModelService.RESPOND_ERROR.equals(result
0692: .get(ModelService.RESPONSE_MESSAGE));
0693:
0694: // validate the context
0695: if (service.validate && !isError && !isFailure) {
0696: try {
0697: service.validate(context,
0698: ModelService.IN_PARAM, locale);
0699: } catch (ServiceValidationException e) {
0700: Debug
0701: .logError(
0702: e,
0703: "Incoming service context (in runAsync: "
0704: + service.name
0705: + ") does not match expected requirements",
0706: module);
0707: throw e;
0708: }
0709: }
0710:
0711: // run the service
0712: if (!isError && !isFailure) {
0713: if (requester != null) {
0714: engine.runAsync(localName, service, context,
0715: requester, persist);
0716: } else {
0717: engine.runAsync(localName, service, context,
0718: persist);
0719: }
0720: engine.sendCallbacks(service, context, null,
0721: GenericEngine.ASYNC_MODE);
0722: }
0723:
0724: if (Debug.timingOn()) {
0725: UtilTimer.closeTimer(localName + " / "
0726: + service.name,
0727: "ASync service finished...", module);
0728: }
0729: checkDebug(service, 0, debugging);
0730: } catch (Throwable t) {
0731: if (Debug.timingOn()) {
0732: UtilTimer.closeTimer(localName + " / "
0733: + service.name, "ASync service failed...",
0734: module);
0735: }
0736: String errMsg = "Service [" + service.name
0737: + "] threw an unexpected exception/error";
0738: Debug.logError(t, errMsg, module);
0739: engine.sendCallbacks(service, context, t,
0740: GenericEngine.ASYNC_MODE);
0741: try {
0742: TransactionUtil.rollback(beganTrans, errMsg, t);
0743: } catch (GenericTransactionException te) {
0744: Debug.logError(te, "Cannot rollback transaction",
0745: module);
0746: }
0747: checkDebug(service, 0, debugging);
0748: if (t instanceof ServiceAuthException) {
0749: throw (ServiceAuthException) t;
0750: } else if (t instanceof ServiceValidationException) {
0751: throw (ServiceValidationException) t;
0752: } else if (t instanceof GenericServiceException) {
0753: throw (GenericServiceException) t;
0754: } else {
0755: throw new GenericServiceException("Service ["
0756: + service.name + "] Failed"
0757: + service.debugInfo(), t);
0758: }
0759: } finally {
0760: // always try to commit the transaction since we don't know in this case if its was an error or not
0761: try {
0762: TransactionUtil.commit(beganTrans);
0763: } catch (GenericTransactionException e) {
0764: Debug.logError(e, "Could not commit transaction",
0765: module);
0766: throw new GenericServiceException(
0767: "Commit transaction failed");
0768: }
0769: }
0770: } catch (GenericTransactionException se) {
0771: Debug.logError(se, "Problems with the transaction", module);
0772: throw new GenericServiceException(
0773: "Problems with the transaction: " + se.getMessage()
0774: + "; See logs for more detail");
0775: } finally {
0776: // resume the parent transaction
0777: if (parentTransaction != null) {
0778: try {
0779: TransactionUtil.resume(parentTransaction);
0780: } catch (GenericTransactionException ise) {
0781: Debug.logError(ise,
0782: "Trouble resuming parent transaction",
0783: module);
0784: throw new GenericServiceException(
0785: "Resume transaction exception: "
0786: + ise.getMessage()
0787: + "; See logs for more detail");
0788: }
0789: }
0790: }
0791: }
0792:
0793: /**
0794: * Run the service asynchronously and IGNORE the result.
0795: * @param localName Name of the context to use.
0796: * @param service Service model object.
0797: * @param context Map of name, value pairs composing the context.
0798: * @param persist True for store/run; False for run.
0799: * @throws ServiceAuthException
0800: * @throws ServiceValidationException
0801: * @throws GenericServiceException
0802: */
0803: public void runAsync(String localName, ModelService service,
0804: Map context, boolean persist) throws ServiceAuthException,
0805: ServiceValidationException, GenericServiceException {
0806: this .runAsync(localName, service, context, null, persist);
0807: }
0808:
0809: /**
0810: * Gets the GenericEngine instance that corresponds to the given name
0811: * @param engineName Name of the engine
0812: * @return GenericEngine instance that corresponds to the engineName
0813: */
0814: public GenericEngine getGenericEngine(String engineName)
0815: throws GenericServiceException {
0816: return factory.getGenericEngine(engineName);
0817: }
0818:
0819: /**
0820: * Gets the JobManager associated with this dispatcher
0821: * @return JobManager that is associated with this dispatcher
0822: */
0823: public JobManager getJobManager() {
0824: return this .jm;
0825: }
0826:
0827: /**
0828: * Gets the JmsListenerFactory which holds the message listeners.
0829: * @return JmsListenerFactory
0830: */
0831: public JmsListenerFactory getJMSListenerFactory() {
0832: return this .jlf;
0833: }
0834:
0835: /**
0836: * Gets the GenericDelegator associated with this dispatcher
0837: * @return GenericDelegator associated with this dispatcher
0838: */
0839: public GenericDelegator getDelegator() {
0840: return this .delegator;
0841: }
0842:
0843: /**
0844: * Gets the Security object associated with this dispatcher
0845: * @return Security object associated with this dispatcher
0846: */
0847: public Security getSecurity() {
0848: return this .security;
0849: }
0850:
0851: /**
0852: * Gets the local context from a name
0853: * @param name of the context to find.
0854: */
0855: public DispatchContext getLocalContext(String name) {
0856: return (DispatchContext) localContext.get(name);
0857: }
0858:
0859: /**
0860: * Gets the local dispatcher from a name
0861: * @param name of the LocalDispatcher to find.
0862: * @return LocalDispatcher matching the loader name
0863: */
0864: public LocalDispatcher getLocalDispatcher(String name) {
0865: return ((DispatchContext) localContext.get(name))
0866: .getDispatcher();
0867: }
0868:
0869: /**
0870: * Test if this dispatcher instance contains the local context.
0871: * @param name of the local context
0872: * @return true if the local context is found in this dispatcher.
0873: */
0874: public boolean containsContext(String name) {
0875: return localContext.containsKey(name);
0876: }
0877:
0878: protected void shutdown() throws GenericServiceException {
0879: Debug.logImportant("Shutting down the service engine...",
0880: module);
0881: // shutdown JMS listeners
0882: jlf.closeListeners();
0883: // shutdown the job scheduler
0884: jm.shutdown();
0885: }
0886:
0887: // checks if parameters were passed for authentication
0888: private Map checkAuth(String localName, Map context,
0889: ModelService origService) throws ServiceAuthException,
0890: GenericServiceException {
0891: String service = ServiceConfigUtil.getElementAttr(
0892: "authorization", "service-name");
0893:
0894: if (service == null) {
0895: throw new GenericServiceException(
0896: "No Authentication Service Defined");
0897: }
0898: if (service.equals(origService.name)) {
0899: // manually calling the auth service, don't continue...
0900: return context;
0901: }
0902:
0903: if (context.containsKey("login.username")) {
0904: // check for a username/password, if there log the user in and make the userLogin object
0905: String username = (String) context.get("login.username");
0906:
0907: if (context.containsKey("login.password")) {
0908: String password = (String) context
0909: .get("login.password");
0910:
0911: context.put("userLogin", getLoginObject(service,
0912: localName, username, password, (Locale) context
0913: .get("locale")));
0914: context.remove("login.password");
0915: } else {
0916: context.put("userLogin", getLoginObject(service,
0917: localName, username, null, (Locale) context
0918: .get("locale")));
0919: }
0920: context.remove("login.username");
0921: } else {
0922: // if a userLogin object is there, make sure the given username/password exists in our local database
0923: GenericValue userLogin = (GenericValue) context
0924: .get("userLogin");
0925:
0926: if (userLogin != null) {
0927: // Because of encrypted passwords we can't just pass in the encrypted version of the password from the data, so we'll do something different and not run the userLogin service...
0928:
0929: //The old way: GenericValue newUserLogin = getLoginObject(service, localName, userLogin.getString("userLoginId"), userLogin.getString("currentPassword"), (Locale) context.get("locale"));
0930: GenericValue newUserLogin = null;
0931: try {
0932: newUserLogin = this
0933: .getDelegator()
0934: .findByPrimaryKeyCache(
0935: "UserLogin",
0936: UtilMisc
0937: .toMap(
0938: "userLoginId",
0939: userLogin
0940: .get("userLoginId")));
0941: } catch (GenericEntityException e) {
0942: Debug.logError(e,
0943: "Error looking up service authentication UserLogin: "
0944: + e.toString(), module);
0945: // leave newUserLogin null, will be handled below
0946: }
0947:
0948: if (newUserLogin == null) {
0949: // uh oh, couldn't validate that one...
0950: // we'll have to remove it from the incoming context which will cause an auth error later if auth is required
0951: Debug
0952: .logInfo(
0953: "Service auth failed for userLoginId ["
0954: + userLogin
0955: .get("userLoginId")
0956: + "] because UserLogin record not found.",
0957: module);
0958: context.remove("userLogin");
0959: } else if (newUserLogin.getString("currentPassword") != null
0960: && !newUserLogin
0961: .getString("currentPassword")
0962: .equals(
0963: userLogin
0964: .getString("currentPassword"))) {
0965: // passwords didn't match, remove the userLogin for failed auth
0966: Debug
0967: .logInfo(
0968: "Service auth failed for userLoginId ["
0969: + userLogin
0970: .get("userLoginId")
0971: + "] because UserLogin record currentPassword fields did not match; note that the UserLogin object passed into a service may need to have the currentPassword encrypted.",
0972: module);
0973: context.remove("userLogin");
0974: }
0975: }
0976: }
0977:
0978: // evaluate permissions for the service or throw exception if fail.
0979: DispatchContext dctx = this .getLocalContext(localName);
0980: if (UtilValidate.isNotEmpty(origService.permissionServiceName)) {
0981: Map permResp = origService.evalPermission(dctx, context);
0982: Boolean hasPermission = (Boolean) permResp
0983: .get("hasPermission");
0984: if (hasPermission == null) {
0985: throw new ServiceAuthException(
0986: "ERROR: the permission-service ["
0987: + origService.permissionServiceName
0988: + "] did not return a result. Not running the service ["
0989: + origService.name + "]");
0990: }
0991: if (hasPermission.booleanValue()) {
0992: context.putAll(permResp);
0993: context = origService.makeValid(context,
0994: ModelService.IN_PARAM);
0995: } else {
0996: String message = (String) permResp.get("failMessage");
0997: if (UtilValidate.isEmpty(message)) {
0998: message = "You do not have permission to invoke the service ["
0999: + origService.name + "]";
1000: }
1001: throw new ServiceAuthException(message);
1002: }
1003: } else {
1004: if (!origService.evalPermissions(dctx, context)) {
1005: throw new ServiceAuthException(
1006: "You do not have permission to invoke the service ["
1007: + origService.name + "]");
1008: }
1009: }
1010:
1011: return context;
1012: }
1013:
1014: // gets a value object from name/password pair
1015: private GenericValue getLoginObject(String service,
1016: String localName, String username, String password,
1017: Locale locale) throws GenericServiceException {
1018: Map context = UtilMisc.toMap("login.username", username,
1019: "login.password", password, "isServiceAuth",
1020: Boolean.TRUE, "locale", locale);
1021:
1022: if (Debug.verboseOn())
1023: Debug
1024: .logVerbose(
1025: "[ServiceDispathcer.authenticate] : Invoking UserLogin Service",
1026: module);
1027:
1028: // get the dispatch context and service model
1029: DispatchContext dctx = getLocalContext(localName);
1030: ModelService model = dctx.getModelService(service);
1031:
1032: // get the service engine
1033: GenericEngine engine = getGenericEngine(model.engineName);
1034:
1035: // invoke the service and get the UserLogin value object
1036: Map result = engine.runSync(localName, model, context);
1037: return (GenericValue) result.get("userLogin");
1038: }
1039:
1040: // checks the locale object in the context
1041: private Locale checkLocale(Map context) {
1042: Object locale = context.get("locale");
1043: Locale newLocale = null;
1044:
1045: if (locale != null) {
1046: if (locale instanceof Locale) {
1047: return (Locale) locale;
1048: } else if (locale instanceof String) {
1049: // en_US = lang_COUNTRY
1050: newLocale = UtilMisc.parseLocale((String) locale);
1051: }
1052: }
1053:
1054: if (newLocale == null) {
1055: newLocale = Locale.getDefault();
1056: }
1057: context.put("locale", newLocale);
1058: return newLocale;
1059: }
1060:
1061: // mode 1 = beginning (turn on) mode 0 = end (turn off)
1062: private boolean checkDebug(ModelService model, int mode,
1063: boolean enable) {
1064: boolean debugOn = Debug.verboseOn();
1065: switch (mode) {
1066: case 0:
1067: if (model.debug && enable && debugOn) {
1068: // turn it off
1069: Debug.set(Debug.VERBOSE, false);
1070: Debug.logInfo("Verbose logging turned OFF", module);
1071: return true;
1072: }
1073: break;
1074: case 1:
1075: if (model.debug && enable && !debugOn) {
1076: // turn it on
1077: Debug.set(Debug.VERBOSE, true);
1078: Debug.logInfo("Verbose logging turned ON", module);
1079: return true;
1080: }
1081: break;
1082: default:
1083: Debug.logError(
1084: "Invalid mode for checkDebug should be (0 or 1)",
1085: module);
1086: }
1087: return false;
1088: }
1089:
1090: // run startup services
1091: private synchronized int runStartupServices() {
1092: if (jm == null)
1093: return 0;
1094:
1095: Element root;
1096: try {
1097: root = ServiceConfigUtil.getXmlRootElement();
1098: } catch (GenericConfigException e) {
1099: Debug.logError(e, module);
1100: return 0;
1101: }
1102:
1103: int servicesScheduled = 0;
1104: List startupServices = UtilXml.childElementList(root,
1105: "startup-service");
1106: if (startupServices != null && startupServices.size() > 0) {
1107: Iterator i = startupServices.iterator();
1108: while (i.hasNext()) {
1109: Element ss = (Element) i.next();
1110: String serviceName = ss.getAttribute("name");
1111: String runtimeDataId = ss
1112: .getAttribute("runtime-data-id");
1113: String delayStr = ss.getAttribute("runtime-delay");
1114: String sendToPool = ss.getAttribute("run-in-pool");
1115: if (UtilValidate.isEmpty(sendToPool)) {
1116: sendToPool = ServiceConfigUtil.getSendPool();
1117: }
1118:
1119: long runtimeDelay;
1120: try {
1121: runtimeDelay = Long.parseLong(delayStr);
1122: } catch (Exception e) {
1123: Debug
1124: .logError(
1125: e,
1126: "Unable to parse runtime-delay value; using 0",
1127: module);
1128: runtimeDelay = 0;
1129: }
1130:
1131: // current time + 1 sec delay + extended delay
1132: long runtime = System.currentTimeMillis() + 1000
1133: + runtimeDelay;
1134: try {
1135: jm.schedule(sendToPool, serviceName, runtimeDataId,
1136: runtime);
1137: } catch (JobManagerException e) {
1138: Debug.logError(e, "Unable to schedule service ["
1139: + serviceName + "]", module);
1140: }
1141: }
1142: }
1143:
1144: return servicesScheduled;
1145: }
1146:
1147: private RunningService logService(String localName,
1148: ModelService modelService, int mode) {
1149: // set up the running service log
1150: RunningService rs = new RunningService(localName, modelService,
1151: mode);
1152: if (runLog == null) {
1153: runLog = new LRUMap(lruLogSize);
1154: }
1155: try {
1156: runLog.put(rs, this );
1157: } catch (Throwable t) {
1158: Debug.logWarning("LRUMap problem; resetting LRU ["
1159: + runLog.size() + "]", module);
1160: runLog = new LRUMap(lruLogSize);
1161: try {
1162: runLog.put(rs, this );
1163: } catch (Throwable t2) {
1164: Debug.logError(t2, "Unable to put() in reset LRU map!",
1165: module);
1166: }
1167: }
1168: return rs;
1169: }
1170:
1171: /**
1172: * Enabled/Disables the Job Manager/Scheduler globally
1173: * (this will not effect any dispatchers already running)
1174: * @param enable
1175: */
1176: public static void enableJM(boolean enable) {
1177: ServiceDispatcher.enableJM = enable;
1178: }
1179:
1180: /**
1181: * Enabled/Disables the JMS listeners globally
1182: * (this will not effect any dispatchers already running)
1183: * @param enable
1184: */
1185: public static void enableJMS(boolean enable) {
1186: ServiceDispatcher.enableJMS = enable;
1187: }
1188:
1189: /**
1190: * Enabled/Disables the startup services globally
1191: * (this will not effect any dispatchers already running)
1192: * @param enable
1193: */
1194: public static void enableSvcs(boolean enable) {
1195: ServiceDispatcher.enableSvcs = enable;
1196: }
1197:
1198: public static Map getServiceLogMap() {
1199: return runLog;
1200: }
1201:
1202: }
|