001: //=== Copyright (C) 2001-2007 Food and Agriculture Organization of the
002: //=== United Nations (FAO-UN), United Nations World Food Programme (WFP)
003: //=== and United Nations Environment Programme (UNEP)
004: //===
005: //=== This program is free software; you can redistribute it and/or modify
006: //=== it under the terms of the GNU General Public License as published by
007: //=== the Free Software Foundation; either version 2 of the License, or (at
008: //=== your option) any later version.
009: //===
010: //=== This program is distributed in the hope that it will be useful, but
011: //=== WITHOUT ANY WARRANTY; without even the implied warranty of
012: //=== MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: //=== General Public License for more details.
014: //===
015: //=== You should have received a copy of the GNU General Public License
016: //=== along with this program; if not, write to the Free Software
017: //=== Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
018: //===
019: //=== Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2,
020: //=== Rome - Italy. email: geonetwork@osgeo.org
021: //==============================================================================
022:
023: package org.fao.geonet.kernel.search;
024:
025: import com.k_int.IR.IRQuery;
026: import com.k_int.IR.InformationFragment;
027: import com.k_int.IR.RecordFormatSpecification;
028: import com.k_int.IR.SearchTask;
029: import com.k_int.IR.TimeoutExceededException;
030: import java.util.Iterator;
031: import java.util.List;
032: import java.util.Vector;
033: import jeeves.exceptions.BadParameterEx;
034: import jeeves.resources.dbms.Dbms;
035: import jeeves.server.ServiceConfig;
036: import jeeves.server.context.ServiceContext;
037: import jeeves.utils.Log;
038: import jeeves.utils.Util;
039: import jeeves.utils.Xml;
040: import org.fao.geonet.constants.Edit;
041: import org.fao.geonet.constants.Geonet;
042: import org.fao.geonet.lib.Lib;
043: import org.jdom.Element;
044: import org.jdom.input.DOMBuilder;
045:
046: //--------------------------------------------------------------------------------
047: // search metadata remotely using Z39.50
048: //--------------------------------------------------------------------------------
049:
050: class Z3950Searcher extends MetaSearcher {
051: public final static int EXECUTING = SearchTask.TASK_EXECUTING;
052: public final static int FAILURE = SearchTask.TASK_FAILURE;
053: public final static int COMPLETE = SearchTask.TASK_COMPLETE;
054: public final static int IDLE = SearchTask.TASK_IDLE;
055:
056: private SearchManager _sm;
057: private String _styleSheetName;
058:
059: private SearchTask _st;
060:
061: //--------------------------------------------------------------------------------
062: // constructor
063: public Z3950Searcher(SearchManager sm, String styleSheetName) {
064: _sm = sm;
065: _styleSheetName = styleSheetName;
066: }
067:
068: //--------------------------------------------------------------------------------
069: // MetaSearcher API
070:
071: public void search(ServiceContext srvContext, Element request,
072: ServiceConfig config) throws Exception {
073: Dbms dbms = (Dbms) srvContext.getResourceManager().open(
074: Geonet.Res.MAIN_DB);
075:
076: Log.debug(Geonet.SEARCH_ENGINE, "CRITERIA:\n"
077: + Xml.getString(request));
078: request.addContent(Lib.db.select(dbms, "Regions", "region"));
079:
080: Element xmlQuery = _sm.transform(_styleSheetName, request);
081:
082: Log.debug(Geonet.SEARCH_ENGINE, "OUTGOING XML QUERY:\n"
083: + Xml.getString(xmlQuery));
084:
085: String query = newQuery(xmlQuery);
086:
087: Log.debug(Geonet.SEARCH_ENGINE, "OUTGOING QUERY: " + query);
088:
089: // get request parameters
090: Vector servers = new Vector();
091: for (Iterator iter = request.getChildren(
092: Geonet.SearchResult.SERVERS).iterator(); iter.hasNext();) {
093: String server = ((Element) iter.next()).getText();
094: servers.add(server);
095: }
096: String sTimeout = request.getChildText("timeout");
097: int timeout;
098: if (sTimeout == null)
099: timeout = 10;
100: else {
101: try {
102: timeout = Integer.parseInt(sTimeout);
103: } catch (NumberFormatException nfe) {
104: throw new IllegalArgumentException(
105: "Bad 'timeout' parameter parameter: "
106: + sTimeout);
107: }
108: }
109: // perform the search
110: // initialize the collection
111: Vector collection_ids = new Vector();
112: for (int i = 0; i < servers.size(); i++) {
113: String name = (String) servers.elementAt(i);
114: collection_ids.add(name);
115: }
116: // build the query
117: IRQuery e = new IRQuery();
118: e.collections = collection_ids;
119: e.query = new com.k_int.IR.QueryModels.PrefixString(query);
120:
121: // create the search task and perform search
122: _st = _sm.getSearchable().createTask(e, null);
123:
124: try {
125: Log.debug(Geonet.SEARCH_ENGINE, "Starting remote search");
126: int status = _st.evaluate(timeout * 1000);
127: Log.debug(Geonet.SEARCH_ENGINE,
128: "Remote search completed. Status is : " + status);
129: } catch (TimeoutExceededException tee) {
130: Log.debug(Geonet.SEARCH_ENGINE,
131: "Raised exception during search: "
132: + tee.getMessage());
133: Log.debug(Geonet.SEARCH_ENGINE, " (C) Stacktrace:"
134: + Util.getStackTrace(tee));
135: }
136: initSearchRange(srvContext);
137: }
138:
139: //-----------------------------------------------------------------------------
140:
141: public Element present(ServiceContext srvContext, Element request,
142: ServiceConfig config) throws Exception {
143: Log.debug(Geonet.SEARCH_ENGINE,
144: "Presenting Z39.50 record for request:\n"
145: + Xml.getString(request));
146: updateSearchRange(request);
147:
148: // get request parameters
149: String syntax = request.getChildText("syntax");
150: if (syntax == null)
151: syntax = config.getValue("syntax", "b");
152:
153: // get results
154: Element response = new Element("response");
155: response.setAttribute("from", getFrom() + "");
156: response.setAttribute("to", getTo() + "");
157:
158: Log.debug(Geonet.SEARCH_ENGINE, "Range is from:" + getFrom()
159: + ", to:" + getTo());
160:
161: Element summary = makeSummary();
162: response.addContent(summary);
163:
164: if (getTo() > 0) {
165: RecordFormatSpecification rfs = new RecordFormatSpecification(
166: "xml", null, syntax);
167: InformationFragment frags[] = _st.getTaskResultSet()
168: .getFragment(getFrom(), (getTo() - getFrom() + 1),
169: rfs);
170: for (int i = 0; i < frags.length; i++) {
171: InformationFragment frag = frags[i];
172: try {
173: DOMBuilder builder = new DOMBuilder();
174:
175: // System.out.println("ORIGINAL OBJECT IN FRAGMENT: " + frag.getOriginalObject()); // DEBUG
176:
177: org.w3c.dom.Document doc = frag.getDocument();
178: org.w3c.dom.Element el = doc.getDocumentElement();
179: Element md = builder.build(el);
180: md.detach();
181:
182: Element info = new Element(Edit.RootChild.INFO,
183: Edit.NAMESPACE);
184: addElement(info, Edit.Info.Elem.ID, (getFrom() + i)
185: + "");
186: addElement(info, Edit.Info.Elem.SERVER, frag
187: .getSourceRepositoryID());
188: md.addContent(info);
189:
190: response.addContent(md);
191: } catch (Exception ex) {
192: // ex.printStackTrace(); // DEBUG
193: Element error = new Element("error");
194: ;
195: error.setAttribute("server", frag
196: .getSourceRepositoryID());
197: error.setAttribute("id", (getFrom() + i) + "");
198: error.setAttribute("message", ex.getClass()
199: .getName()
200: + ": " + ex.getMessage());
201: response.addContent(error);
202: }
203: }
204: }
205: Log.debug(Geonet.SEARCH_ENGINE, "Presented metadata is:\n"
206: + Xml.getString(response));
207:
208: return response;
209: }
210:
211: //-----------------------------------------------------------------------------
212:
213: public int getSize() {
214: return _st.getTaskResultSet().getFragmentCount();
215: }
216:
217: //-----------------------------------------------------------------------------
218:
219: public Element getSummary() {
220: Element response = new Element("response");
221: response.addContent(makeSummary());
222: return response;
223: }
224:
225: //-----------------------------------------------------------------------------
226: /** closes the connection(s)
227: */
228: public void close() {
229: if (_st != null) {
230: _st.destroyTask();
231: _st = null;
232: }
233: }
234:
235: //--------------------------------------------------------------------------------
236: // private methods
237:
238: // makes a new query
239: private String newQuery(Element xmlQuery) throws Exception {
240: String name = xmlQuery.getName();
241: if (name.equals("query")) {
242: String attrset = xmlQuery.getAttributeValue("attrset");
243: List children = xmlQuery.getChildren();
244: if (children.size() == 0)
245: throw new BadParameterEx("Z59.50-query", Xml
246: .getString(xmlQuery));
247:
248: Element child = (Element) children.get(0);
249: return "@attrset " + attrset + " " + newQuery(child);
250: } else if (name.equals("and") || name.equals("or")
251: || name.equals("not")) {
252: Element leftChild = (Element) xmlQuery.getChildren().get(0);
253: Element rightChild = (Element) xmlQuery.getChildren()
254: .get(1);
255: return "@" + name + " " + newQuery(leftChild) + " "
256: + newQuery(rightChild);
257: } else if (name.equals("term")) {
258: String use = xmlQuery.getAttributeValue("use");
259: String structure = xmlQuery.getAttributeValue("structure");
260: String relation = xmlQuery.getAttributeValue("relation");
261: String text = xmlQuery.getText();
262:
263: StringBuffer term = new StringBuffer();
264: if (use != null)
265: term.append("@attr 1=" + use + " ");
266: if (structure != null)
267: term.append("@attr 4=" + structure + " ");
268: if (relation != null)
269: term.append("@attr 2=" + relation + " ");
270: boolean toQuote = !isAlpha(text);
271: if (toQuote)
272: term.append('"');
273: term.append(text);
274: if (toQuote)
275: term.append('"');
276: return term.toString();
277: } else
278: throw new Exception("unknown Z39.50 query type: " + name);
279: }
280:
281: //-----------------------------------------------------------------------------
282:
283: private boolean isAlpha(String text) {
284: for (int i = 0; i < text.length(); i++)
285: if (!Character.isLetter(text.charAt(i)))
286: return false;
287: return true;
288: }
289:
290: //-----------------------------------------------------------------------------
291:
292: private Element makeSummary() {
293: Element summary = new Element("summary");
294: summary.setAttribute("count", getSize() + "");
295: summary.setAttribute("status", getStatus());
296: summary.setAttribute("type", "remote");
297:
298: return summary;
299: }
300:
301: //-----------------------------------------------------------------------------
302: /** returns the current status
303: */
304: private String getStatus() {
305: switch (_st.getTaskStatusCode()) {
306: case SearchTask.TASK_COMPLETE:
307: return "complete";
308: case SearchTask.TASK_EXECUTING:
309: return "executing";
310: case SearchTask.TASK_FAILURE:
311: return "failure";
312: case SearchTask.TASK_IDLE:
313: return "idle";
314: }
315: return null;
316: }
317: }
|