0001: /**********************************************************************************
0002: *
0003: * Copyright (c) 2003, 2004 The Regents of the University of Michigan, Trustees of Indiana University,
0004: * Board of Trustees of the Leland Stanford, Jr., University, and The MIT Corporation
0005: *
0006: * Licensed under the Educational Community License Version 1.0 (the "License");
0007: * By obtaining, using and/or copying this Original Work, you agree that you have read,
0008: * understand, and will comply with the terms and conditions of the Educational Community License.
0009: * You may obtain a copy of the License at:
0010: *
0011: * http://cvs.sakaiproject.org/licenses/license_1_0.html
0012: *
0013: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
0014: * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
0015: * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
0016: * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
0017: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0018: *
0019: **********************************************************************************/package edu.indiana.lib.twinpeaks.search.singlesearch.web2;
0020:
0021: import edu.indiana.lib.twinpeaks.net.*;
0022: import edu.indiana.lib.twinpeaks.search.*;
0023: import edu.indiana.lib.twinpeaks.search.singlesearch.CqlParser;
0024: import edu.indiana.lib.twinpeaks.util.*;
0025:
0026: import java.io.*;
0027: import java.net.*;
0028: import java.util.*;
0029:
0030: import javax.xml.parsers.*;
0031: import javax.xml.transform.*;
0032: import javax.xml.transform.stream.StreamResult;
0033: import javax.xml.transform.stream.StreamSource;
0034:
0035: import org.w3c.dom.*;
0036: import org.xml.sax.*;
0037:
0038: /**
0039: * Send a query to the Muse Web2 interface
0040: */
0041: public class Web2Query extends HttpTransactionQueryBase {
0042:
0043: private static org.apache.commons.logging.Log _log = LogUtils
0044: .getLog(Web2Query.class);
0045: /**
0046: * Records displayed "per page"
0047: */
0048: public static final String RECORDS_PER_PAGE = "10";
0049: /**
0050: * Records to fetch from each search target
0051: */
0052: private static final String RECORDS_PER_TARGET = "30";
0053: /**
0054: * Unique name for this search application
0055: */
0056: private final String APPLICATION = SessionContext
0057: .uniqueSessionName(this );
0058: /**
0059: * Web2 Bridge error code: No logged-in session
0060: */
0061: private static final String NO_SESSION = "904";
0062: /**
0063: * Database for this request
0064: */
0065: private String _database;
0066: /**
0067: * Muse syntax search criteria for this request (see parseRequest())
0068: */
0069: private String _museSearchString;
0070: /**
0071: * Web2 input
0072: */
0073: private Document _web2Document;
0074: /**
0075: * Active reference ID #
0076: */
0077: private static long _referenceId = System.currentTimeMillis();
0078: /**
0079: * Local ID (for the current transaction)
0080: */
0081: private long _transactionId;
0082: /**
0083: * Local version of server response (modified to contain SFX URL data)
0084: */
0085: private byte _localResponseBytes[];
0086: /**
0087: * Local byte array ready for use?
0088: */
0089: private boolean _localResponseBytesReady = false;
0090: /**
0091: * General synchronization
0092: */
0093: private static Object _sync = new Object();
0094:
0095: /**
0096: * Constructor
0097: */
0098: public Web2Query() {
0099: super ();
0100: }
0101:
0102: /**
0103: * Parse user request parameters.
0104: * @param parameterMap Request details (name=value pairs)
0105: */
0106: public void parseRequest(Map parameterMap) {
0107: String action;
0108:
0109: super .parseRequest(parameterMap);
0110: /*
0111: * These cannot be null by the time we get here
0112: */
0113: if ((getRequestParameter("guid") == null)
0114: || (getRequestParameter("url") == null)) {
0115: throw new IllegalArgumentException("Missing GUID or URL");
0116: }
0117:
0118: action = getRequestParameter("action");
0119: if ("startsearch".equalsIgnoreCase(action)) {
0120: if ((getRequestParameter("targets") == null)
0121: || (getRequestParameter("username") == null)
0122: || (getRequestParameter("password") == null)) {
0123: throw new IllegalArgumentException(
0124: "Missing target list, username, or password");
0125: }
0126: }
0127: /*
0128: * Now deal with the search criteria (CQL syntax)
0129: */
0130: _museSearchString = parseCql(getRequestParameter("searchString"));
0131: }
0132:
0133: /**
0134: * Search
0135: */
0136: public void doQuery() {
0137: Document document;
0138: String action;
0139:
0140: /*
0141: * Get the logical "database" (a name for the configuration for this search)
0142: */
0143: _database = getRequestParameter("database");
0144: /*
0145: * We'll manage redirects, and submit with POST
0146: */
0147: setRedirectBehavior(REDIRECT_MANAGED);
0148: setQueryMethod(METHOD_POST);
0149: /*
0150: * Save the URL and query text
0151: */
0152: setUrl(getRequestParameter("url"));
0153: setSearchString(getSearchString());
0154: /*
0155: * Request additional results (pagination)? Save the requested
0156: * pagesize and starting record
0157: */
0158: action = getRequestParameter("action");
0159: if (action.equalsIgnoreCase("requestRange")) {
0160: getSessionContext().putInt(
0161: "startRecord",
0162: getIntegerRequestParameter("startRecord")
0163: .intValue());
0164: getSessionContext().putInt("pageSize",
0165: getIntegerRequestParameter("pageSize").intValue());
0166: }
0167: /*
0168: * New search?
0169: */
0170: if (action.equalsIgnoreCase("startSearch")) { /*
0171: * Initialize a new session context block
0172: */
0173: StatusUtils.initialize(getSessionContext(),
0174: getRequestParameter("targets"));
0175: /*
0176: * LOGOFF any previous session
0177: */
0178: clearParameters();
0179:
0180: doLogoffCommand();
0181: submit();
0182:
0183: try {
0184: _log.debug(DomUtils.serialize(getResponseDocument()));
0185: } catch (Exception ignore) {
0186: }
0187: /*
0188: * LOGON
0189: */
0190: clearParameters();
0191:
0192: doLogonCommand();
0193: submit();
0194: validateResponse("LOGON");
0195: /*
0196: * FIND
0197: */
0198: clearParameters();
0199:
0200: doFindCommand();
0201: submit();
0202: validateResponse("FIND");
0203: setFindStatus();
0204:
0205: try {
0206: _log.debug("Search response:");
0207: _log.debug(DomUtils.serialize(getResponseDocument()));
0208: } catch (Exception ignore) {
0209: }
0210:
0211: /*
0212: doSearchCommand();
0213: submit();
0214: validateResponse("SEARCH");
0215: setSearchStatus();
0216:
0217: try
0218: {
0219: System.out.println();
0220: System.out.println(DomUtils.serialize(getResponseDocument()));
0221: System.out.println();
0222: } catch (Exception ignore) { }
0223: */
0224: return;
0225: }
0226: /*
0227: * Request additional SEARCH results
0228: */
0229: /*
0230: System.out.println("Result request starts");
0231: clearParameters();
0232:
0233: doResultsCommand(getTransactionResultSetName());
0234: submit();
0235: validateResponse("RESULTS");
0236: */
0237: /*
0238: * Request FIND results
0239: */
0240: clearParameters();
0241:
0242: doResultsCommand(getFindResultSetId());
0243: submit();
0244: validateResponse("RESULTS");
0245: /*
0246: * Combine results
0247: */
0248: /*
0249: System.out.println("Combine request starts");
0250: clearParameters();
0251:
0252: doCombineCommand();
0253: submit();
0254: validateResponse("COMBINE");
0255:
0256: try
0257: {
0258: System.out.println();
0259: System.out.println(DomUtils.serialize(getResponseDocument()));
0260: System.out.println();
0261: }
0262: catch (Exception ignore) { }
0263:
0264: System.out.println("Result request starts");
0265: clearParameters();
0266:
0267: doResultsCommand(getTransactionResultSetName());
0268: submit();
0269: validateResponse("RESULTS");
0270:
0271: try
0272: {
0273: System.out.println();
0274: System.out.println(DomUtils.serialize(getResponseDocument()));
0275: System.out.println();
0276: }
0277: catch (Exception ignore) { }
0278: */
0279: }
0280:
0281: /**
0282: * Custom submit behavior (override HttpTransactionQueryBase)
0283: */
0284: public int submit() {
0285:
0286: setWeb2InputMessage();
0287: return super .submit();
0288: }
0289:
0290: /*
0291: * Helpers
0292: */
0293:
0294: /**
0295: * Generate a LOGON command
0296: */
0297: private void doLogonCommand() throws SearchException {
0298: Element logonElement;
0299: String username, password;
0300:
0301: username = getRequestParameter("username");
0302: password = getRequestParameter("password");
0303:
0304: try {
0305: doWeb2InputHeader();
0306:
0307: logonElement = addWeb2Input("LOGON");
0308: addWeb2Input(logonElement, "USER_ID", username);
0309: addWeb2Input(logonElement, "USER_PWD", password);
0310:
0311: doWeb2InputClose();
0312:
0313: } catch (DomException exception) {
0314: throw new SearchException(exception.toString());
0315: }
0316: }
0317:
0318: /**
0319: * Generate a LOGOFF command
0320: */
0321: private void doLogoffCommand() throws SearchException {
0322:
0323: try {
0324: doWeb2InputHeader();
0325: addWeb2Input("LOGOFF");
0326: doWeb2InputClose();
0327:
0328: } catch (DomException exception) {
0329: throw new SearchException(exception.toString());
0330: }
0331: }
0332:
0333: /**
0334: * Generate a SEARCH command
0335: */
0336: private void doSearchCommand() throws SearchException {
0337: Element element, searchElement;
0338: String sortBy, targets;
0339:
0340: /*
0341: * Pick up the database(s) to examine, sort mode
0342: */
0343: targets = getRequestParameter("targets");
0344: _log.debug("Targets for search source " + _database + ": "
0345: + targets);
0346:
0347: _log.debug("SEARCH FOR: " + getSearchString());
0348:
0349: sortBy = getRequestParameter("sortBy");
0350: if (StringUtils.isNull(sortBy)) {
0351: sortBy = "ICERankingKeyRelevance";
0352: }
0353: _log.debug("RANKING_KEY: " + sortBy);
0354: /*
0355: * And generate the search command
0356: */
0357: try {
0358: doWeb2InputHeader();
0359:
0360: searchElement = addWeb2Input("SEARCH");
0361: addWeb2Input(searchElement, "TERMS", getSearchString());
0362: addWeb2Input(searchElement, "QUERY_TYPE", "Muse");
0363: addWeb2Input(searchElement, "TARGETS", targets);
0364: addWeb2Input(searchElement, "START", "1");
0365: addWeb2Input(searchElement, "PER_TARGET",
0366: RECORDS_PER_TARGET);
0367: addWeb2Input(searchElement, "PER_PAGE",
0368: getIntegerRequestParameter("pageSize").toString());
0369: addWeb2Input(searchElement, "RESULT_SET",
0370: getTransactionResultSetName());
0371: addWeb2Input(searchElement, "APPEND", "false");
0372: addWeb2Input(searchElement, "JITTERBUG_KEY");
0373:
0374: element = addWeb2Input(searchElement, "DEDUPE_KEY");
0375: element.setAttribute("dedupeMode", "");
0376: element.setAttribute("dedupeMixMode", "");
0377:
0378: element = addWeb2Input(searchElement, "RANKING_KEY", sortBy);
0379: element.setAttribute("rankingMode", "");
0380: element.setAttribute("rankingOrder", "");
0381:
0382: doWeb2InputClose();
0383:
0384: } catch (DomException exception) {
0385: throw new SearchException(exception.toString());
0386: }
0387: }
0388:
0389: /**
0390: * Generate a FIND command
0391: */
0392: private void doFindCommand() throws SearchException {
0393: Element element, searchElement;
0394: String pageSize, sortBy, targets;
0395: String searchCriteria, searchFilter;
0396: int active, targetCount;
0397:
0398: /*
0399: * Set search criteria (use the search filter, if any is configured)
0400: */
0401: searchFilter = SearchSource.getConfiguredParameter(_database,
0402: "searchFilter");
0403: searchCriteria = (searchFilter == null) ? ""
0404: : (searchFilter + " ");
0405: searchCriteria += getSearchString();
0406:
0407: /*
0408: * Pick up the database(s) to examine, sort mode
0409: */
0410: targets = getRequestParameter("targets");
0411: targetCount = new StringTokenizer(targets).countTokens();
0412:
0413: _log.debug("Targets for search source " + _database + ", "
0414: + targetCount + " targets: " + targets);
0415: _log.debug("Search for: " + searchCriteria);
0416:
0417: sortBy = getRequestParameter("sortBy");
0418: if (StringUtils.isNull(sortBy)) {
0419: sortBy = "ICERankingKeyRelevance";
0420: }
0421: sortBy = "";
0422: _log.debug("RANKING_KEY: " + sortBy);
0423:
0424: pageSize = getIntegerRequestParameter("pageSize").toString();
0425: _log.debug("PAGE SIZE: " + pageSize);
0426:
0427: /*
0428: * And generate the FIND command
0429: */
0430: try {
0431: doWeb2InputHeader();
0432:
0433: searchElement = addWeb2Input("FIND");
0434: addWeb2Input(searchElement, "TERMS", searchCriteria);
0435: addWeb2Input(searchElement, "QUERY_TYPE", "Muse");
0436: addWeb2Input(searchElement, "TARGETS", targets);
0437: addWeb2Input(searchElement, "FIND_SET", "sakaibrary");
0438: addWeb2Input(searchElement, "JITTERBUG_KEY");
0439:
0440: addWeb2Input(searchElement, "PER_PAGE", pageSize);
0441: addWeb2Input(searchElement, "PER_TARGET", pageSize);
0442:
0443: element = addWeb2Input(searchElement, "DEDUPE_KEY", "");
0444: element.setAttribute("dedupeMode", "");
0445: element.setAttribute("dedupeMixMode", "");
0446:
0447: element = addWeb2Input(searchElement, "RANKING_KEY", sortBy);
0448: element.setAttribute("rankingMode", "");
0449: element.setAttribute("rankingOrder", "");
0450:
0451: doWeb2InputClose();
0452:
0453: saveFindReferenceId(getTransactionId());
0454:
0455: } catch (DomException exception) {
0456: throw new SearchException(exception.toString());
0457: }
0458: }
0459:
0460: /**
0461: * Generate a STATUS command
0462: */
0463: private void doStatusCommand() throws SearchException {
0464: Element statusElement;
0465:
0466: try {
0467: doWeb2InputHeader();
0468:
0469: statusElement = addWeb2Input("STATUS");
0470: addWeb2Input(statusElement, "ID", getFindReferenceId());
0471:
0472: doWeb2InputClose();
0473:
0474: } catch (DomException exception) {
0475: throw new SearchException(exception.toString());
0476: }
0477: }
0478:
0479: /**
0480: * Generate a COMBINE command
0481: */
0482: private void doCombineCommand() throws SearchException {
0483: Element combineElement;
0484:
0485: _log.debug("COMBINE find sets: " + getFindResultSetId());
0486: _log.debug("COMBINE output: " + getTransactionResultSetName());
0487:
0488: try {
0489: Element element;
0490:
0491: doWeb2InputHeader();
0492:
0493: combineElement = addWeb2Input("COMBINE");
0494: addWeb2Input(combineElement, "RESULT_SET",
0495: getFindResultSetId());
0496: addWeb2Input(combineElement, "OUTPUT_RESULT_SET",
0497: getTransactionResultSetName());
0498:
0499: doWeb2InputClose();
0500:
0501: } catch (DomException exception) {
0502: throw new SearchException(exception.toString());
0503: }
0504: }
0505:
0506: /**
0507: * Generate a RESULTS command
0508: */
0509: private void doResultsCommand(String resultSetId)
0510: throws SearchException {
0511: Element resultsElement;
0512: int active, start, pageSize, perTarget;
0513:
0514: active = getSessionContext().getInt("active");
0515: start = getSessionContext().getInt("startRecord");
0516: pageSize = getSessionContext().getInt("pageSize");
0517: perTarget = pageSize;
0518:
0519: _log.debug("Results commmand: " + resultSetId);
0520: _log.debug("Active = " + active + ", start record = " + start
0521: + ", page size = " + pageSize + ", per=target = "
0522: + perTarget);
0523:
0524: try {
0525: doWeb2InputHeader();
0526:
0527: resultsElement = addWeb2Input("RESULTS");
0528: addWeb2Input(resultsElement, "START", String.valueOf(start));
0529: addWeb2Input(resultsElement, "PER_PAGE", String
0530: .valueOf(pageSize));
0531: addWeb2Input(resultsElement, "PER_TARGET", String
0532: .valueOf(perTarget));
0533: addWeb2Input(resultsElement, "RESULT_SET", resultSetId);
0534:
0535: doWeb2InputClose();
0536:
0537: } catch (DomException exception) {
0538: throw new SearchException(exception.toString());
0539: }
0540: }
0541:
0542: /**
0543: * Create the Web2 input Document, add the standard Web2 XML header
0544: */
0545: private void doWeb2InputHeader() throws DomException {
0546: setTransactionId();
0547: _web2Document = DomUtils.createXmlDocument("MUSEWEB2-INPUT");
0548: }
0549:
0550: /**
0551: * Format the standard Web2 XML close
0552: */
0553: private void doWeb2InputClose() {
0554: addReferenceId();
0555: }
0556:
0557: /**
0558: * Fetch the (Muse format) search string (overrides HttpTransactionQueryBase)
0559: * @return The native Muse query text
0560: */
0561: public String getSearchString() {
0562: return _museSearchString;
0563: }
0564:
0565: /**
0566: * Parse CQL search queries into a crude take on the Muse format.
0567: * @param cql String containing a cql query
0568: * @return Muse search criteria
0569: */
0570: private String parseCql(String cql) throws IllegalArgumentException {
0571: CqlParser parser;
0572: String result;
0573:
0574: _log.debug("Initial CQL Criteria: " + cql);
0575:
0576: parser = new CqlParser();
0577: result = parser.doCQL2MetasearchCommand(cql);
0578:
0579: _log.debug("Processed Result: " + result);
0580: return result;
0581: }
0582:
0583: /**
0584: * Merge the STATUS and RESULTS response documents
0585: */
0586: private void mergeResponseDocuments(Document statusDocument,
0587: Document resultsDocument) {
0588: _localResponseBytesReady = false;
0589:
0590: try {
0591: Element statusElement = DomUtils.getElement(statusDocument
0592: .getDocumentElement(), "STATUS");
0593:
0594: DomUtils.copyDocumentNode(statusElement, resultsDocument);
0595:
0596: _localResponseBytes = DomUtils.serialize(resultsDocument)
0597: .getBytes("UTF-8");
0598: _localResponseBytesReady = true;
0599:
0600: } catch (Exception exception) {
0601: throw new SearchException(exception.toString());
0602: }
0603: }
0604:
0605: /**
0606: * Set the xmlMessage parameter (this is the "command" sent to Web2)
0607: * @param xml XML command
0608: */
0609: private void setWeb2InputMessage() throws SearchException {
0610: try {
0611: setParameter("xmlMessage", DomUtils
0612: .serialize(_web2Document));
0613:
0614: } catch (DomException exception) {
0615: throw new SearchException(exception.toString());
0616: }
0617: }
0618:
0619: /**
0620: * Format the current reference id
0621: * @return XML id
0622: */
0623: private Element addReferenceId() {
0624: return addWeb2Input("REFERENCE_ID", getTransactionId());
0625: }
0626:
0627: /**
0628: * Establish a transaction ID for the current activity (LOGIN, SEARCH, etc)
0629: */
0630: private void setTransactionId() {
0631: synchronized (_sync) {
0632: _transactionId = _referenceId++;
0633: }
0634: }
0635:
0636: /**
0637: * Fetch the current transaction id
0638: * @return The ID
0639: */
0640: private String getTransactionId() {
0641: return Long.toHexString(_transactionId);
0642: }
0643:
0644: /**
0645: * Returns a new result set name for this transaction
0646: * @return Result set name (constant portion + reference ID)
0647: */
0648: private synchronized String saveFindReferenceId(String transactionId) {
0649: removeSessionParameter(APPLICATION, "findReferenceId");
0650: setSessionParameter(APPLICATION, "findReferenceId",
0651: transactionId);
0652:
0653: return getFindReferenceId();
0654: }
0655:
0656: /**
0657: * Returns a new result set name for this transaction
0658: * @return Result set name (constant portion + reference ID)
0659: */
0660: private synchronized String getFindReferenceId() {
0661: return getSessionParameter(APPLICATION, "findReferenceId");
0662: }
0663:
0664: public Iterator getStatusMapEntrySetIterator() {
0665: HashMap statusMap = (HashMap) getSessionContext().get(
0666: "searchStatus");
0667: Set entrySet = statusMap.entrySet();
0668:
0669: return entrySet.iterator();
0670: }
0671:
0672: /**
0673: * Returns a new result set name for this transaction
0674: * @return Active result set name(s) (name1|name2|name3), null if none active
0675: */
0676: private String getFindResultSetId() {
0677: String ids = "";
0678: int active = 0;
0679:
0680: for (Iterator iterator = getStatusMapEntrySetIterator(); iterator
0681: .hasNext();) {
0682: Map.Entry entry = (Map.Entry) iterator.next();
0683: HashMap systemMap = (HashMap) entry.getValue();
0684: String status = (String) systemMap.get("STATUS");
0685: String id;
0686:
0687: if (!status.equals("ACTIVE")) {
0688: continue;
0689: }
0690:
0691: id = (String) systemMap.get("RESULT_SET");
0692:
0693: if (ids.length() == 0) {
0694: ids = id;
0695: } else {
0696: ids = ids + "|" + id;
0697: }
0698: active++;
0699: }
0700: _log.debug(active + " result set ids: " + ids);
0701: getSessionContext().putInt("active", active);
0702: return (ids.length() == 0) ? null : ids;
0703: }
0704:
0705: /**
0706: * Returns a new result set name for this transaction
0707: * @return Result set name (constant portion + reference ID)
0708: */
0709: private synchronized String getNewTransactionResultSetName() {
0710:
0711: removeSessionParameter(APPLICATION, "resultSetName");
0712: return getTransactionResultSetName();
0713: }
0714:
0715: /**
0716: * Returns the result set name for this transaction (SEARCH)
0717: * @return Result set name (constant portion + reference ID)
0718: */
0719: private synchronized String getTransactionResultSetName() {
0720: String resultSetName = getSessionParameter(APPLICATION,
0721: "resultSetName");
0722:
0723: if (resultSetName == null) {
0724: StringBuffer name = new StringBuffer("sakaibrary");
0725:
0726: name.append(getTransactionId());
0727: name.append(".xml");
0728:
0729: resultSetName = name.toString();
0730: setSessionParameter(APPLICATION, "resultSetName",
0731: resultSetName);
0732: }
0733: _log.debug("Transaction result set name: " + resultSetName);
0734: return resultSetName;
0735: }
0736:
0737: /**
0738: * Add Element and child text
0739: * @param parentElement Add new element here
0740: * @param newElementName New element name
0741: * @param text Child text (for the new element)
0742: */
0743: private Element addWeb2Input(Element parentElement,
0744: String newElementName, String text) {
0745: Element element;
0746:
0747: element = DomUtils.createElement(parentElement, newElementName);
0748:
0749: if (!StringUtils.isNull(text)) {
0750: DomUtils.addText(element, text);
0751: }
0752: return element;
0753: }
0754:
0755: /**
0756: * Add Element and child text to document root
0757: * @param newElementName New element name
0758: * @param text Child text (for the new element)
0759: */
0760: private Element addWeb2Input(String newElementName, String text) {
0761:
0762: return addWeb2Input(_web2Document.getDocumentElement(),
0763: newElementName, text);
0764: }
0765:
0766: /**
0767: * Add Element to parent
0768: * @param parentElement Add new element here
0769: * @param newElementName New element name
0770: */
0771: private Element addWeb2Input(Element parentElement,
0772: String newElementName) {
0773:
0774: return addWeb2Input(parentElement, newElementName, null);
0775: }
0776:
0777: /**
0778: * Add Element to document root
0779: * @param newElementName New element name
0780: */
0781: private Element addWeb2Input(String newElementName) {
0782:
0783: return addWeb2Input(_web2Document.getDocumentElement(),
0784: newElementName, null);
0785: }
0786:
0787: /**
0788: * Get an element from the server response
0789: * @Element parent Look for named element here
0790: * @param elementName Element name
0791: * @return The first occurance of the named element (null if none)
0792: */
0793: private Element getElement(Element parent, String elementName) {
0794: try {
0795: Element root = parent;
0796:
0797: if (root == null) {
0798: root = getResponseDocument().getDocumentElement();
0799: }
0800: return DomUtils.getElement(root, elementName);
0801:
0802: } catch (Exception exception) {
0803: throw new SearchException(exception.toString());
0804: }
0805: }
0806:
0807: /**
0808: * Get an element from the server response (search from document root)
0809: * @param elementName Element name
0810: * @return The first occurance of the named element (null if none)
0811: */
0812: private Element getElement(String elementName) {
0813: return getElement(null, elementName);
0814: }
0815:
0816: /**
0817: * Initial response validation. Verify:
0818: * <ul>
0819: * <li>Error code
0820: * <li>Correct <REFERENCE_ID> value
0821: * </ul>
0822: *<p>
0823: * @param action Server activity (SEARCH, LOGON, etc)
0824: */
0825: private void validateResponse(String action) throws SearchException {
0826: Document document;
0827: Element element;
0828: String error, id, message, status;
0829:
0830: document = getResponseDocument();
0831: element = getElement(document.getDocumentElement(), action);
0832: error = element.getAttribute("ERROR");
0833: status = element.getAttribute("STATUS");
0834:
0835: element = getElement(document.getDocumentElement(),
0836: "REFERENCE_ID");
0837: id = DomUtils.getText(element);
0838:
0839: if (!"false".equalsIgnoreCase(error)) {
0840: String text = "Error " + error + ", status = " + status
0841: + ", for activity " + action;
0842:
0843: LogUtils.displayXml(_log, text, document);
0844:
0845: if (status.equals(NO_SESSION)) {
0846: /*
0847: * Session timeout is a special case
0848: * o Re-initialize (clear the query URL)
0849: * o Set "global failure" status
0850: * o Throw the exception
0851: */
0852: removeQueryUrl(APPLICATION);
0853: StatusUtils.setGlobalError(getSessionContext(), status,
0854: "Session timed out");
0855: throw new SessionTimeoutException();
0856: }
0857:
0858: element = getElement(document.getDocumentElement(), "DATA");
0859: if ((message = DomUtils.getText(element)) == null) {
0860: message = "";
0861: }
0862:
0863: StatusUtils.setGlobalError(getSessionContext(), status,
0864: message);
0865:
0866: if (!StringUtils.isNull(message)) {
0867: text = "Error " + status + ": " + message;
0868: }
0869: throw new SearchException(text);
0870: }
0871:
0872: if (!getTransactionId().equalsIgnoreCase(id)) {
0873: String text = "Transaction ID mismatch, expected "
0874: + getTransactionId() + ", found " + id;
0875:
0876: LogUtils.displayXml(_log, text, document);
0877: StatusUtils.setGlobalError(getSessionContext(),
0878: "<internal>", text);
0879:
0880: throw new SearchException(text);
0881: }
0882: }
0883:
0884: /**
0885: * Save the initial status (find set name(s), estimated hits, etc.) as
0886: * session context information
0887: * @return A Map of status details (keyed by target name)
0888: */
0889: private void setFindStatus() throws SearchException {
0890: NodeList nodeList;
0891: String target;
0892: int active, total;
0893:
0894: nodeList = DomUtils.getElementList(getResponseDocument()
0895: .getDocumentElement(), "RECORD");
0896: active = 0;
0897: total = 0;
0898:
0899: /*
0900: * Update the status map for each target
0901: */
0902: for (int i = 0; i < nodeList.getLength(); i++) {
0903: Element recordElement = (Element) nodeList.item(i);
0904: HashMap map;
0905:
0906: String text;
0907: Element element;
0908: int estimate;
0909:
0910: element = DomUtils.getElement(recordElement, "TARGET");
0911: target = DomUtils.getText(element);
0912: map = StatusUtils.getStatusMapForTarget(
0913: getSessionContext(), target);
0914:
0915: element = DomUtils.getElement(recordElement, "RESULT_SET");
0916: text = DomUtils.getText(element);
0917: map.put("RESULT_SET", ((text == null) ? "<none>" : text));
0918:
0919: element = DomUtils.getElement(recordElement, "ESTIMATE");
0920: text = DomUtils.getText(element);
0921: map.put("ESTIMATE", text);
0922:
0923: estimate = Integer.parseInt(text);
0924: total += estimate;
0925:
0926: map.put("STATUS", "DONE");
0927: if (estimate > 0) {
0928: map.put("STATUS", "ACTIVE");
0929: active++;
0930: }
0931: }
0932: /*
0933: * Save in session context:
0934: *
0935: * -- The largest number of records we could possibly return
0936: * -- The count of "in progress" searches
0937: */
0938: getSessionContext().put("maxRecords", String.valueOf(total));
0939: getSessionContext().putInt("active", active);
0940: }
0941:
0942: /**
0943: * Save the initial SEARCH command status (find set name, estimated hits)
0944: * @return A Map of status details (keyed by target name)
0945: */
0946: private void setSearchStatus() throws SearchException {
0947: List nodeList;
0948: String target;
0949: int active, total;
0950:
0951: nodeList = DomUtils.selectElementsByAttributeValue(
0952: getResponseDocument().getDocumentElement(), "RECORD",
0953: "type", "status");
0954: active = 0;
0955: total = 0;
0956:
0957: for (int i = 0; i < nodeList.size(); i++) {
0958: Element recordElement = (Element) nodeList.get(i);
0959: HashMap map;
0960: String text;
0961: Element element;
0962: int max;
0963:
0964: target = getSourceId(recordElement.getAttribute("source"));
0965: if (target.equals("unavailable")) {
0966: target = recordElement.getAttribute("source");
0967: }
0968:
0969: map = StatusUtils.getStatusMapForTarget(
0970: getSessionContext(), target);
0971: map.put("RESULT_SET", getTransactionResultSetName());
0972: map.put("HITS", "0");
0973:
0974: element = DomUtils.getElement(recordElement, "ESTIMATE");
0975: text = DomUtils.getText(element);
0976: map.put("ESTIMATE", text);
0977:
0978: max = Integer.parseInt(text);
0979: total += max;
0980:
0981: map.put("STATUS", "DONE");
0982: if (max > 0) {
0983: map.put("STATUS", "ACTIVE");
0984: active++;
0985: }
0986: }
0987: /*
0988: * Save in session context:
0989: *
0990: * -- The largest number of records we could possibly return
0991: * -- The count of "in progress" searches
0992: */
0993: getSessionContext().put("maxRecords", String.valueOf(total));
0994: getSessionContext().putInt("active", active);
0995: }
0996:
0997: /**
0998: * Look up the "sourceID" attribute (the target name) for a specified
0999: * RECORD element "source"
1000: * @param source Source attribute text
1001: * @return The sourceID attribute
1002: */
1003: private String getSourceId(String source) {
1004: NodeList nodeList;
1005:
1006: nodeList = DomUtils.getElementList(getResponseDocument()
1007: .getDocumentElement(), "RECORD");
1008:
1009: for (int i = 0; i < nodeList.getLength(); i++) {
1010: Element recordElement = (Element) nodeList.item(i);
1011:
1012: if (source.equals(recordElement.getAttribute("source"))) {
1013: return recordElement.getAttribute("sourceID");
1014: }
1015: }
1016: return "unavailable";
1017: }
1018: }
|