001: /*--
002:
003: Copyright (C) 2002-2005 Adrian Price.
004: All rights reserved.
005:
006: Redistribution and use in source and binary forms, with or without
007: modification, are permitted provided that the following conditions
008: are met:
009:
010: 1. Redistributions of source code must retain the above copyright
011: notice, this list of conditions, and the following disclaimer.
012:
013: 2. Redistributions in binary form must reproduce the above copyright
014: notice, this list of conditions, and the disclaimer that follows
015: these conditions in the documentation and/or other materials
016: provided with the distribution.
017:
018: 3. The names "OBE" and "Open Business Engine" must not be used to
019: endorse or promote products derived from this software without prior
020: written permission. For written permission, please contact
021: adrianprice@sourceforge.net.
022:
023: 4. Products derived from this software may not be called "OBE" or
024: "Open Business Engine", nor may "OBE" or "Open Business Engine"
025: appear in their name, without prior written permission from
026: Adrian Price (adrianprice@users.sourceforge.net).
027:
028: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
029: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
030: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
031: DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
032: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
033: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
034: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
035: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
036: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
037: IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
038: POSSIBILITY OF SUCH DAMAGE.
039:
040: For more information on OBE, please see
041: <http://obe.sourceforge.net/>.
042:
043: */
044:
045: package org.wfmc.sample;
046:
047: import org.apache.commons.logging.Log;
048: import org.apache.commons.logging.LogFactory;
049: import org.obe.engine.EngineContext;
050: import org.obe.spi.service.ServiceManager;
051: import org.w3c.dom.Document;
052: import org.w3c.dom.Element;
053:
054: import java.util.HashMap;
055: import java.util.Map;
056:
057: /**
058: * Implements Tools used by the WfMC Sample Workflow Process.
059: * <p/>
060: * <em>Caveat emptor:</em> Like the WfMC sample workflow itself, this class
061: * implements a simplistic treatment of the problem domain. There's no
062: * synchronization, transactionality, persistence, etc. In other words, don't
063: * try and use any of this stuff as the basis for a real-life application!
064: *
065: * @author Adrian Price
066: */
067: public class WfMCSampleTools {
068: private static final Log _logger = LogFactory
069: .getLog(WfMCSampleTools.class);
070: private static final String XML_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
071: private static final String VALID_DATA = "ValidData";
072: private static final String INVALID_DATA = "InvalidData";
073: private static final String ACCEPT = "Accept";
074: private static final String OVER_LIMIT = "OverLimit";
075: private static final String BAD_DATA_FORMAT = "BadDataFormat";
076: private static final String MERCHANT_NUMBER = "OBE2004";
077: private static final int RECEIVED = 0;
078: private static final int PROCESSING = 1;
079: private static final int CANCELLED = 2;
080: private static final int RESERVED = 3;
081: private static final int BILLED = 4;
082: private static final int FULFILLED = 5;
083: public static final String[][] _accountInfo = {
084: { "123456", "10000", "0" }, { "234567", "20000", "0" },
085: { "345678", "30000", "0" }, { "456789", "40000", "0" }, };
086: private static final Map _accounts = new HashMap();
087: private static final Map _orders = new HashMap();
088: private static final Map _creditInfoMap = new HashMap();
089: private static int _orderNumber;
090:
091: static {
092: for (int i = 0; i < _accountInfo.length; i++) {
093: String[] s = _accountInfo[i];
094: _accounts.put(s[0], new Account(s));
095: }
096: }
097:
098: private static final class Account {
099: final int accountNumber;
100: double creditLimit;
101: double balance;
102:
103: Account(String[] s) {
104: accountNumber = Integer.parseInt(s[0]);
105: creditLimit = Double.parseDouble(s[1]);
106: balance = Double.parseDouble(s[2]);
107: }
108: }
109:
110: private static final class OrderStatus {
111: int status;
112: final Document orderInfo;
113:
114: public OrderStatus(Document orderInfo) {
115: status = RECEIVED;
116: this .orderInfo = orderInfo;
117: }
118: }
119:
120: /**
121: * Transforms a string representing an order into an XML document.
122: *
123: * @param orderString
124: * @return An XML document conforming to the Order element as defined in
125: * orderschema.xsd.
126: */
127: public static Document transformData(String orderString) {
128: _logger.info("transformData(" + orderString + ')');
129:
130: // Parse the order into an XML document.
131: // TODO: We need a means of validating against a specified schema.
132: ServiceManager svcMgr = EngineContext.peekContext()
133: .getServiceManager();
134: return svcMgr.getDataConverter().toDocument(orderString);
135: }
136:
137: /**
138: * Validates an order.
139: *
140: * @param orderInfo An instance of the Order type as defined in
141: * orderschema.xsd.
142: * @return An string conforming to the OrderType schema defined inline
143: * in wfmc-sample.xpdl. The string is restricted to: ValidData,InvalidData,
144: * Accept,BadCredit,OverLimit,BadDataFormat.
145: */
146: public static String checkData(Document orderInfo) {
147: _logger.info("checkData(" + orderInfo + ')');
148:
149: // Determine order status.
150: String statusString;
151: try {
152: String accountNumber = orderInfo.getDocumentElement()
153: .getAttribute("accountNumber");
154: boolean acnumValid = _accounts.get(accountNumber) != null;
155: if (acnumValid) {
156: statusString = VALID_DATA;
157: } else {
158: statusString = INVALID_DATA;
159: }
160: } catch (NumberFormatException e) {
161: statusString = BAD_DATA_FORMAT;
162: }
163:
164: return statusString;
165: }
166:
167: /**
168: * Checks a vendor [what does that mean?]. Why are we talking about
169: * vendors? This is a Sales Order Processing workflow not a Purchase Order
170: * Processing workflow - our concern is with customers, not vendors.
171: *
172: * @param accountNumber The [vendor's?] account number.
173: * @param amount The total amount of the order.
174: * @return An string conforming to the OrderType schema defined inline
175: * in wfmc-sample.xpdl. The string is restricted to: ValidData,InvalidData,
176: * Accept,BadCredit,OverLimit,BadDataFormat.
177: */
178: public static String checkVendor(int accountNumber, double amount) {
179: _logger.info("checkVendor(" + accountNumber + ", " + amount
180: + ')');
181:
182: String statusString = INVALID_DATA;
183:
184: // Determine customer account status.
185: Account account = (Account) _accounts.get(String
186: .valueOf(accountNumber));
187: if (account != null) {
188: double balance = account.balance + amount;
189: if (balance > account.creditLimit)
190: statusString = OVER_LIMIT;
191: else
192: statusString = ACCEPT;
193: }
194:
195: return statusString;
196: }
197:
198: /**
199: * Enters order details.
200: *
201: * @param orderInfo An XML document conforming to the Order element as
202: * defined in orderschema.xsd.
203: * @return A newly allocated order number.
204: */
205: public static int enterOrder(Document orderInfo) {
206: _logger.info("enterOrder(" + orderInfo + ')');
207:
208: int orderNumber = _orderNumber++;
209: _orders.put(new Integer(orderNumber),
210: new OrderStatus(orderInfo));
211: return orderNumber;
212: }
213:
214: // TODO: Huh??? The composed message isn't returned anywhere...
215:
216: /**
217: * Composes a message.
218: *
219: * @param status An XML document conforming to the OrderType schema defined
220: * inline in wfmc-sample.xpdl.
221: * @param orderNumber The order number.
222: */
223: public static void composeMessage(String status, int orderNumber) {
224: _logger.info("composeMessage(" + status + ", " + orderNumber
225: + ')');
226: }
227:
228: /**
229: * Creates and initializes a CreditInfo object.
230: *
231: * @param accountNumber Account number to check.
232: * @param amount Amount to authorize.
233: * @param cardType As defined by CardType in orderSchema.xsd
234: * @return An XML document conforming to the CreditInfo element defined in
235: * creditService.wsdl.
236: */
237: public static Document setCreditInfo(int accountNumber,
238: double amount, String cardType) {
239:
240: _logger.info("setCreditInfo(" + accountNumber + ", " + amount
241: + ", " + cardType + ')');
242:
243: // Build the XML document.
244: StringBuffer sb = new StringBuffer();
245: sb.append(XML_HEADER).append('\n');
246: sb
247: .append(
248: "<CreditInfo xmlns=\"http://wfmc.org/standards/docs/xpdl_sample\">")
249: .append('\n');
250: sb.append(" <merchantNumber>").append(MERCHANT_NUMBER).append(
251: "</merchantNumber>").append('\n');
252: sb.append(" <accountNumber>").append(accountNumber).append(
253: "</accountNumber>").append('\n');
254: sb.append(" <amount>").append(amount).append("</amount>")
255: .append('\n');
256: sb.append(" <cardType>").append(cardType)
257: .append("</cardType>").append('\n');
258: sb.append("</CreditInfo>");
259: ServiceManager svcMgr = EngineContext.peekContext()
260: .getServiceManager();
261: Document creditInfo = svcMgr.getDataConverter().toDocument(sb);
262:
263: _creditInfoMap.put(String.valueOf(accountNumber), creditInfo);
264:
265: _logger.info("setCreditInfo returned: " + creditInfo);
266:
267: return creditInfo;
268: }
269:
270: /**
271: * Returns the credit info for the specified order.
272: * <br/>
273: * [WfMC Description was wrong (copy/paste from shipOrder)]
274: *
275: * @param orderNumber The order number.
276: * @return An XML document conforming to the CreditInfo element defined in
277: * creditService.wsdl.
278: */
279: public static Document getCreditInfo(int orderNumber) {
280: _logger.info("getCreditInfo(" + orderNumber + ')');
281:
282: OrderStatus orderStatus = (OrderStatus) _orders
283: .get(new Integer(orderNumber));
284: if (orderStatus == null)
285: return null;
286: String accountNumber = orderStatus.orderInfo
287: .getDocumentElement().getAttribute("accountNumber");
288: return (Document) _creditInfoMap.get(accountNumber);
289: }
290:
291: /**
292: * This application presents a screen that presents order information for
293: * the order identified by shipOrder. The user may update the order with any
294: * changes such as back order information. It returns a string indicating
295: * whether the order is complete or on back order.
296: *
297: * @param orderNumber The order number.
298: * @return Order status: "Complete" or "Backorder".
299: */
300: public static String shipOrder(int orderNumber) {
301: _logger.info("shipOrder(" + orderNumber + ')');
302:
303: // Odd numbered orders will be complete, even ones on back order.
304: return (orderNumber & 0x1) == 0x1 ? "Complete" : "BackOrder";
305: }
306:
307: /**
308: * Charges the credit card and prepares a receipt for a credit order.
309: *
310: * @param orderNumber The order number.
311: */
312: public static void billAccount(int orderNumber) {
313: _logger.info("billAccount(" + orderNumber + ')');
314:
315: OrderStatus orderStatus = ((OrderStatus) _orders
316: .get(new Integer(orderNumber)));
317: if (orderStatus != null) {
318: Element docElt = orderStatus.orderInfo.getDocumentElement();
319: String accountNumber = docElt.getAttribute("accountNumber");
320: Account account = (Account) _accounts.get(accountNumber);
321: if (account != null) {
322: account.balance += Float.parseFloat(docElt
323: .getAttribute("totalAmount"));
324: orderStatus.status = BILLED;
325: }
326: }
327: }
328:
329: /**
330: * Creates an invoice using the order information and stores it on a server.
331: *
332: * @param orderNumber The order number.
333: * @return The WEBDAV URL of the resulting invoice.
334: */
335: public static String createInvoice(int orderNumber) {
336: _logger.info("createInvoice(" + orderNumber + ')');
337:
338: return "http://localhost:8080/slide/files/invoice"
339: + orderNumber + ".html";
340: }
341:
342: /**
343: * Creates a receipt using the order information and stores it on a server.
344: *
345: * @param orderNumber The order number.
346: * @return The WEBDAV URL of the resulting receipt.
347: */
348: public static String createReceipt(int orderNumber) {
349: _logger.info("createReceipt(" + orderNumber + ')');
350:
351: return "http://localhost:8080/slide/files/receipt"
352: + orderNumber + ".html";
353: }
354:
355: /**
356: * [WfMC activity description was wrong - copy/paste from shipOrder.]
357: * Cancels an order.
358: *
359: * @param orderNumber The order number.
360: */
361: public static void cancelOrder(int orderNumber) {
362: _logger.info("cancelOrder(" + orderNumber + ')');
363:
364: OrderStatus orderStatus = ((OrderStatus) _orders
365: .get(new Integer(orderNumber)));
366: if (orderStatus != null)
367: orderStatus.status = CANCELLED;
368: }
369:
370: /**
371: * Converts status returned by credit check to OrderStatus.
372: *
373: * @param creditStatus
374: * @return The order status.
375: */
376: public static String setOrderStatus(String creditStatus) {
377: _logger.info("setOrderStatus(" + creditStatus + ')');
378:
379: return creditStatus;
380: }
381: }
|