001: /*
002: * The contents of this file are subject to the
003: * Mozilla Public License Version 1.1 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
006: *
007: * Software distributed under the License is distributed on an "AS IS"
008: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
009: * See the License for the specific language governing rights and
010: * limitations under the License.
011: *
012: * The Initial Developer of the Original Code is Simulacra Media Ltd.
013: * Portions created by Simulacra Media Ltd are Copyright (C) Simulacra Media Ltd, 2004.
014: *
015: * All Rights Reserved.
016: *
017: * Contributor(s):
018: */
019:
020: package org.openharmonise.dav.server.managers;
021:
022: import java.net.URL;
023: import java.text.*;
024: import java.util.*;
025: import java.util.logging.*;
026:
027: import javax.xml.parsers.DocumentBuilderFactory;
028:
029: import org.openharmonise.commons.dsi.*;
030: import org.openharmonise.commons.xml.namespace.NamespaceType;
031: import org.openharmonise.dav.server.utils.*;
032: import org.openharmonise.rm.*;
033: import org.openharmonise.rm.dsi.DataStoreInterfaceFactory;
034: import org.openharmonise.rm.factory.HarmoniseFactoryException;
035: import org.openharmonise.rm.metadata.*;
036: import org.openharmonise.rm.resources.*;
037: import org.openharmonise.rm.resources.content.TextResource;
038: import org.openharmonise.rm.resources.metadata.properties.*;
039: import org.openharmonise.rm.resources.metadata.properties.ranges.DateRange;
040: import org.openharmonise.rm.search.Search;
041: import org.w3c.dom.Element;
042:
043: import com.ibm.webdav.*;
044: import com.ibm.webdav.basicsearch.BasicSearchRequest;
045: import com.ibm.webdav.impl.*;
046:
047: /**
048: * This class handles the implementation of the DASL functionality for the Harmonise
049: * repository.
050: *
051: * @author Michael Bell
052: * @version $Revision: 1.2 $
053: *
054: */
055: public class HarmoniseSearchManager implements SearchManager {
056: public static final String W3C_XMLSCHEMA_URI = "http://www.w3.org/2001/XMLSchema";
057: private AbstractDataStoreInterface m_dsi = null;
058:
059: private static final Logger m_logger = Logger
060: .getLogger(HarmoniseSearchManager.class.getName());;
061:
062: public HarmoniseSearchManager() {
063: }
064:
065: public void initialize() {
066:
067: try {
068: this .m_dsi = DataStoreInterfaceFactory
069: .getDataStoreInterface();
070: } catch (Exception e) {
071: throw new RuntimeException(
072: "Datastore interface not started properly.");
073: }
074: }
075:
076: public SearchSchema getSearchSchema(SearchRequest searchReq)
077: throws WebDAVException {
078: SearchSchema schema = null;
079:
080: try {
081: schema = searchReq.getSearchSchema();
082:
083: try {
084: org.w3c.dom.Document document = DocumentBuilderFactory
085: .newInstance().newDocumentBuilder()
086: .newDocument();
087:
088: Element contentLength = document.createElementNS(
089: NamespaceType.DAV.getURI(), "contentlength");
090: contentLength.setPrefix(NamespaceType.DAV.getPrefix());
091:
092: Element intEl = document.createElementNS(
093: NamespaceType.XML_SCHEMA.getURI(), "integer");
094: intEl.setPrefix(NamespaceType.XML_SCHEMA.getPrefix());
095:
096: Element resourceType = document.createElementNS(
097: NamespaceType.DAV.getURI(), "resourcetype");
098: resourceType.setPrefix(NamespaceType.DAV.getPrefix());
099:
100: Element stringEl = document.createElementNS(
101: NamespaceType.XML_SCHEMA.getURI(), "string");
102: stringEl
103: .setPrefix(NamespaceType.XML_SCHEMA.getPrefix());
104:
105: Element lastModifiedDate = document.createElementNS(
106: NamespaceType.DAV.getURI(), "getlastmodified");
107: lastModifiedDate.setPrefix(NamespaceType.DAV
108: .getPrefix());
109:
110: Element dateEl = document.createElementNS(
111: NamespaceType.XML_SCHEMA.getURI(), "date");
112: dateEl.setPrefix(NamespaceType.XML_SCHEMA.getPrefix());
113:
114: Element contentType = document.createElementNS(
115: NamespaceType.DAV.getURI(), "getcontenttype");
116: contentType.setPrefix(NamespaceType.DAV.getPrefix());
117:
118: schema.addPropertyDescription(contentLength, intEl,
119: false, true, false);
120:
121: schema.addPropertyDescription(resourceType, stringEl,
122: false, true, false);
123:
124: schema.addPropertyDescription(lastModifiedDate, dateEl,
125: false, true, true);
126:
127: schema.addPropertyDescription(contentType, stringEl,
128: false, true, true);
129: } catch (Exception e) {
130: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
131: }
132:
133: schema.addAnyOtherPropertyDescription(true, true, true);
134: } catch (Exception e) {
135: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
136: throw new WebDAVException(
137: WebDAVStatus.SC_INTERNAL_SERVER_ERROR,
138: "Had trouble getting schema");
139: }
140:
141: return schema;
142: }
143:
144: /**
145: * Get the results of search
146: *
147: * @return a Vector of Resources (ResourceImpl or CollectionImpl)
148: * @exception com.ibm.webdav.WebDAVException
149: */
150: public Vector executeSearch(SearchRequest searchReq,
151: ResourceImpl resource) throws WebDAVException {
152:
153: Vector resources = new Vector();
154:
155: try {
156: AbstractChildObject child = getWorkflowObject(resource);
157:
158: AbstractParentObject parent = null;
159:
160: if (child != null) {
161: if (child instanceof AbstractParentObject) {
162: parent = (AbstractParentObject) child;
163: } else {
164: parent = child.getRealParent();
165: }
166: }
167:
168: Vector selectProps = searchReq.getSelectProperties();
169: SearchCondition condition = searchReq.getCondition();
170:
171: //this will either be a absolute ref or a ref relative to the request URI
172: String scope_href = searchReq.getScopeURI();
173:
174: if ((scope_href != null) && (scope_href.length() > 0)) {
175: if (scope_href.startsWith("http")) {
176: parent = null;
177: } else if (scope_href.startsWith("ftp")) {
178: parent = null;
179: } else if (scope_href.startsWith("/")) {
180:
181: AbstractChildObject scope_wflw = HarmoniseNameResolver
182: .getObjectFromURL(m_dsi, scope_href);
183:
184: if (scope_wflw instanceof AbstractParentObject) {
185: parent = (AbstractParentObject) scope_wflw;
186: } else {
187: parent = scope_wflw.getRealParent();
188: }
189: } else {
190: StringBuffer sPath = new StringBuffer();
191: sPath.append(resource.getURL().getPath());
192:
193: if (sPath.toString().endsWith("/") == false) {
194: sPath.append("/");
195: }
196:
197: sPath.append(scope_href);
198:
199: AbstractChildObject scope_wflw = HarmoniseNameResolver
200: .getObjectFromURL(m_dsi, sPath.toString());
201:
202: if (scope_wflw instanceof AbstractParentObject) {
203: parent = (AbstractParentObject) scope_wflw;
204: } else {
205: parent = scope_wflw.getRealParent();
206: }
207: }
208: }
209:
210: //if scope uri didn't result in a valid sec throw bad request
211: if (parent == null) {
212: throw new WebDAVException(WebDAVStatus.SC_BAD_REQUEST,
213: "Invalid scope");
214: }
215:
216: int scope_depth = searchReq.getScopeDepth();
217:
218: Vector allChildren = new Vector();
219:
220: List childTypes = parent.getChildClassNames();
221:
222: Iterator iter = childTypes.iterator();
223:
224: boolean bFirstTime = true;
225:
226: while (iter.hasNext()) {
227: Search oh_search = new Search(this .m_dsi);
228: oh_search.cacheResults(false);
229:
230: int nLimit = searchReq.getResultLimit();
231:
232: if (nLimit > 0) {
233: oh_search.setResultSize(nLimit);
234: }
235:
236: oh_search.addConditionGroup(parent);
237:
238: if (scope_depth < 0) {
239: oh_search.addSubGroupsAsConditions(parent);
240: }
241:
242: String sChildClass = (String) iter.next();
243:
244: AbstractChildObject emptyObj = (AbstractChildObject) Class
245: .forName(sChildClass).newInstance();
246:
247: // setting to search docs
248: oh_search.setSearchType(emptyObj);
249:
250: //add conditions if first time around
251: try {
252: addSearchConditions(oh_search, condition);
253: } catch (InvalidSearchObjectException e) {
254: continue;
255: }
256:
257: // reset select props
258: addSelectProperties(oh_search, selectProps);
259:
260: // reset order by props
261: addOrdering(oh_search, searchReq);
262:
263: // search docs
264:
265: List results = oh_search.executeSearch();
266: allChildren.addAll(results);
267: bFirstTime = false;
268: }
269:
270: for (int i = 0; i < allChildren.size(); i++) {
271: AbstractChildObject rdoc = (AbstractChildObject) allChildren
272: .elementAt(i);
273:
274: if (HarmoniseNameResolver.hasValidDAVPath(rdoc)) {
275:
276: URL rsrc_url = new URL(HarmoniseNameResolver
277: .getFullURL(rdoc, resource.getURL()));
278:
279: ResourceImpl member = null;
280:
281: if (rdoc instanceof AbstractParentObject) {
282: member = new CollectionImpl(rsrc_url,
283: HarmoniseNameResolver
284: .getRealPath(rsrc_url), null);
285: } else {
286: member = new ResourceImpl(rsrc_url,
287: HarmoniseNameResolver
288: .getRealPath(rsrc_url));
289: }
290:
291: member.setRequestContext(resource.getContext());
292: resources.addElement(member);
293: }
294: }
295: } catch (WebDAVException e) {
296: throw e;
297: } catch (Exception e) {
298: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
299: throw new WebDAVException(
300: WebDAVStatus.SC_INTERNAL_SERVER_ERROR, e
301: .getMessage());
302: }
303:
304: return resources;
305: }
306:
307: private void addSelectProperties(Search oh_search,
308: Vector selectProps) throws DataAccessException,
309: HarmoniseFactoryException, DataStoreException {
310: AbstractProfiledObject profObj = (AbstractProfiledObject) oh_search
311: .getSearchType();
312: Profile prof = new Profile(this .m_dsi, profObj);
313:
314: for (int i = 0; i < selectProps.size(); i++) {
315: PropertyName propName = (PropertyName) selectProps
316: .elementAt(i);
317: String sPropertyName = propName.getLocal();
318:
319: if (propName.getNamespace().equals(
320: NamespaceType.OHRM.getURI())) {
321: if ((sPropertyName.equalsIgnoreCase("id") == false)
322: && (sPropertyName.equals("status") == false)) {
323: // creating propInst with doc profile so search has to be on docs first
324: AbstractPropertyInstance propInst = PropertyInstanceFactory
325: .getPropertyInstance(m_dsi, PropertyFactory
326: .getPropertyFromName(m_dsi,
327: sPropertyName));
328:
329: oh_search.addSelectProperty(propInst);
330: } else if (sPropertyName.equals("status") == true) {
331: oh_search.addSelectColumn(profObj
332: .getInstanceColumnRef(
333: AbstractEditableObject.TAG_STATUS,
334: false));
335: }
336: } else if (propName.getNamespace().equals(
337: NamespaceType.DAV.getURI())) {
338:
339: if (HarmoniseNameResolver
340: .isMappedLiveProperty(sPropertyName) == true) {
341: ColumnRef colref = profObj
342: .getInstanceColumnRef(
343: HarmoniseNameResolver
344: .getTagForLiveProperty(sPropertyName),
345: false);
346:
347: oh_search.addSelectColumn(colref);
348: }
349: }
350: }
351: }
352:
353: private void addSearchConditions(Search oh_search, Object condition)
354: throws HarmoniseFactoryException, DataAccessException,
355: PopulateException, NameResolverException, WebDAVException,
356: DataStoreException, InvalidSearchObjectException {
357: if (condition instanceof SearchCondition) {
358: SearchCondition srchCond = (SearchCondition) condition;
359: String sOp = srchCond.getOperator();
360: oh_search.setStringingOperator(sOp);
361: for (int i = 0; i < srchCond.size(); i++) {
362: try {
363: addSearchConditions(oh_search, srchCond
364: .getCondition(i));
365: } catch (InvalidSearchObjectException e) {
366: if (BasicSearchRequest.OPERATOR_AND
367: .equalsIgnoreCase(sOp)) {
368: throw e;
369: }
370: }
371: }
372: } else if (condition instanceof SearchPropertyConditionTerm) {
373: SearchPropertyConditionTerm term = (SearchPropertyConditionTerm) condition;
374:
375: if (term.getPropertyName() != null
376: && term.getPropertyName().getNamespace()
377: .equalsIgnoreCase(
378: NamespaceType.OHRM.getURI())) {
379: String sPropertyName = term.getPropertyLocalName();
380:
381: if ((sPropertyName.equalsIgnoreCase("id") == false)
382: && (sPropertyName.equalsIgnoreCase("status") == false)
383: && (sPropertyName.equalsIgnoreCase("title") == false)
384: && (sPropertyName
385: .equalsIgnoreCase("description") == false)) {
386: Profile prof = new Profile(this .m_dsi,
387: (AbstractProfiledObject) oh_search
388: .getSearchType());
389:
390: Property prop = PropertyFactory
391: .getPropertyFromName(m_dsi, sPropertyName);
392: if (prop != null) {
393: AbstractPropertyInstance propInst = PropertyInstanceFactory
394: .getPropertyInstance(m_dsi, prop);
395: propInst.setProfile(prof);
396:
397: propInst.setOperator(term.getOperator());
398: if (propInst instanceof GeneralPropertyInstance) {
399: if (prop.getRange() instanceof DateRange) {
400: String sDateFormat = "yyyy-MM-dd";
401: String sDateTimeFormat = "yyyy-MM-dd HH:mm:ss";
402:
403: String sTermVal = term.getValue();
404:
405: SimpleDateFormat formatter = new SimpleDateFormat();
406:
407: if (sTermVal.length() == sDateFormat
408: .length()) {
409: formatter.applyPattern(sDateFormat);
410: } else if (sTermVal.length() == sDateTimeFormat
411: .length()) {
412: formatter
413: .applyPattern(sDateTimeFormat);
414: }
415:
416: try {
417: Date dt = formatter.parse(sTermVal);
418: ((GeneralPropertyInstance) propInst)
419: .addValue(dt);
420: } catch (ParseException e) {
421: throw new WebDAVException(
422: WebDAVStatus.SC_BAD_REQUEST,
423: "Date not formatted correctly in search condition term");
424: }
425:
426: } else {
427: ((GeneralPropertyInstance) propInst)
428: .addValue(term.getValue());
429: }
430:
431: } else if (propInst instanceof ChildObjectPropertyInstance) {
432: AbstractChildObject child = HarmoniseNameResolver
433: .getObjectFromURL(m_dsi, term
434: .getValue());
435: if (child != null) {
436: ((ChildObjectPropertyInstance) propInst)
437: .addValue(child);
438: }
439:
440: } else if (propInst instanceof ProfilePropertyInstance) {
441: throw new WebDAVException(
442: WebDAVStatus.SC_BAD_REQUEST,
443: "Can't search on this property - "
444: + sPropertyName);
445: }
446:
447: if (propInst.hasValues()) {
448: oh_search.addConditionProperty(propInst);
449: }
450: } else {
451: throw new WebDAVException(
452: WebDAVStatus.SC_BAD_REQUEST,
453: "Can't search on this property - "
454: + sPropertyName);
455: }
456: } else if (sPropertyName.equalsIgnoreCase("title") == true) {
457: ColumnRef nameCol = oh_search
458: .getSearchType()
459: .getInstanceColumnRef(
460: AbstractEditableObject.TAG_DISPLAY_NAME,
461: false);
462: Vector vec = new Vector();
463: vec.add(term.getValue());
464: oh_search.addConditionColumn(nameCol, term
465: .getOperator(), vec);
466:
467: }
468: }
469: } else {
470: if (condition instanceof SearchConditionTerm
471: && oh_search.getSearchType() instanceof TextResource) {
472:
473: try {
474: SearchConditionTerm term = (SearchConditionTerm) condition;
475:
476: ColumnRef contentCol = oh_search.getSearchType()
477: .getInstanceColumnRef(
478: TextResource.TAG_CONTENT, false);
479: Vector vec = new Vector();
480: vec.add(term.getValue());
481: oh_search.addConditionColumn(contentCol, term
482: .getOperator(), vec);
483:
484: } catch (InvalidColumnReferenceException e) {
485: //ignore and don't add the term if it's an invalid column
486: m_logger.log(Level.WARNING,
487: e.getLocalizedMessage(), e);
488: }
489: } else {
490: throw new InvalidSearchObjectException();
491: }
492:
493: }
494: }
495:
496: private void addOrdering(Search oh_search, SearchRequest searchReq)
497: throws DataStoreException, DataAccessException,
498: HarmoniseFactoryException {
499: Vector orderBys = searchReq.getOrderByProperties();
500:
501: if (orderBys != null) {
502: for (int i = 0; i < orderBys.size(); i++) {
503: PropertyName pname = (PropertyName) orderBys
504: .elementAt(i);
505: String sOrderDirection = searchReq
506: .getOrderByDirection(pname);
507: String sPropertyName = pname.getLocal();
508:
509: if (pname.getNamespace().equalsIgnoreCase(
510: NamespaceType.OHRM.getURI())) {
511: if (sPropertyName.equalsIgnoreCase("id") == true) {
512: oh_search
513: .setOrderBy(
514: oh_search
515: .getSearchType()
516: .getInstanceColumnRef(
517: AbstractEditableObject.ATTRIB_ID,
518: false),
519: sOrderDirection);
520: } else if (sPropertyName.equalsIgnoreCase("status") == true) {
521: oh_search
522: .setOrderBy(
523: oh_search
524: .getSearchType()
525: .getInstanceColumnRef(
526: AbstractEditableObject.TAG_STATUS,
527: false),
528: sOrderDirection);
529: } else {
530: Profile prof = new Profile(this .m_dsi,
531: (AbstractProfiledObject) oh_search
532: .getSearchType());
533: AbstractPropertyInstance propInst = PropertyInstanceFactory
534: .getPropertyInstance(m_dsi,
535: PropertyFactory
536: .getPropertyFromName(
537: m_dsi,
538: sPropertyName));
539: propInst.setProfile(prof);
540:
541: oh_search.setOrderBy(propInst, sOrderDirection);
542: }
543: } else if (pname.getNamespace().equalsIgnoreCase(
544: NamespaceType.DAV.getURI())) {
545: if (HarmoniseNameResolver
546: .isMappedLiveProperty(sPropertyName) == true) {
547: ColumnRef colref = oh_search
548: .getSearchType()
549: .getInstanceColumnRef(
550: HarmoniseNameResolver
551: .getTagForLiveProperty(sPropertyName),
552: false);
553: oh_search.setOrderBy(colref, sOrderDirection);
554: }
555: }
556: }
557: }
558: }
559:
560: private AbstractChildObject getWorkflowObject(ResourceImpl resource)
561: throws WebDAVException {
562: AbstractChildObject child = null;
563:
564: try {
565: URL url = resource.getURL();
566:
567: if (HarmoniseNameResolver.isDAVRoot(url) == false
568: && HarmoniseNameResolver
569: .isArchivedVirtualCollection(url) == false) {
570: child = HarmoniseNameResolver.getObjectFromURL(m_dsi,
571: resource.getURL());
572: }
573: } catch (Exception e) {
574: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
575: throw new WebDAVException(
576: WebDAVStatus.SC_INTERNAL_SERVER_ERROR,
577: "Problem occurred");
578: }
579:
580: return child;
581: }
582:
583: public boolean validate(SearchRequest searchReq)
584: throws WebDAVException {
585: //TODO implement validation
586: return true;
587: }
588: }
|