001 // $HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/portal/standard/csw/control/SimpleSearchListener.java $ 002 /*---------------------------------------------------------------------------- 003 This file is part of deegree, http://deegree.org/ 004 Copyright (C) 2001-2009 by: 005 - Department of Geography, University of Bonn - 006 and 007 - lat/lon GmbH - 008 009 This library is free software; you can redistribute it and/or modify it under 010 the terms of the GNU Lesser General Public License as published by the Free 011 Software Foundation; either version 2.1 of the License, or (at your option) 012 any later version. 013 This library is distributed in the hope that it will be useful, but WITHOUT 014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 016 details. 017 You should have received a copy of the GNU Lesser General Public License 018 along with this library; if not, write to the Free Software Foundation, Inc., 019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 020 021 Contact information: 022 023 lat/lon GmbH 024 Aennchenstr. 19, 53177 Bonn 025 Germany 026 http://lat-lon.de/ 027 028 Department of Geography, University of Bonn 029 Prof. Dr. Klaus Greve 030 Postfach 1147, 53001 Bonn 031 Germany 032 http://www.geographie.uni-bonn.de/deegree/ 033 034 e-mail: info@deegree.org 035 ----------------------------------------------------------------------------*/ 036 037 package org.deegree.portal.standard.csw.control; 038 039 import java.io.BufferedReader; 040 import java.io.InputStream; 041 import java.io.InputStreamReader; 042 import java.io.Reader; 043 import java.io.StringReader; 044 import java.net.URL; 045 import java.util.ArrayList; 046 import java.util.HashMap; 047 import java.util.Iterator; 048 import java.util.List; 049 import java.util.Map; 050 051 import javax.servlet.http.HttpServletRequest; 052 import javax.servlet.http.HttpSession; 053 054 import org.apache.commons.httpclient.HttpClient; 055 import org.apache.commons.httpclient.methods.PostMethod; 056 import org.apache.commons.httpclient.methods.StringRequestEntity; 057 import org.deegree.enterprise.WebUtils; 058 import org.deegree.enterprise.control.AbstractListener; 059 import org.deegree.enterprise.control.FormEvent; 060 import org.deegree.enterprise.control.RPCException; 061 import org.deegree.enterprise.control.RPCFactory; 062 import org.deegree.enterprise.control.RPCMember; 063 import org.deegree.enterprise.control.RPCMethodCall; 064 import org.deegree.enterprise.control.RPCParameter; 065 import org.deegree.enterprise.control.RPCStruct; 066 import org.deegree.enterprise.control.RPCWebEvent; 067 import org.deegree.framework.log.ILogger; 068 import org.deegree.framework.log.LoggerFactory; 069 import org.deegree.framework.util.CharsetUtils; 070 import org.deegree.framework.util.StringTools; 071 import org.deegree.framework.xml.DOMPrinter; 072 import org.deegree.framework.xml.NamespaceContext; 073 import org.deegree.framework.xml.XMLFragment; 074 import org.deegree.framework.xml.XMLParsingException; 075 import org.deegree.framework.xml.XMLTools; 076 import org.deegree.i18n.Messages; 077 import org.deegree.ogcbase.CommonNamespaces; 078 import org.deegree.portal.standard.csw.CatalogClientException; 079 import org.deegree.portal.standard.csw.MetadataTransformer; 080 import org.deegree.portal.standard.csw.configuration.CSWClientConfiguration; 081 import org.deegree.portal.standard.csw.model.DataSessionRecord; 082 import org.w3c.dom.Document; 083 import org.w3c.dom.Element; 084 import org.w3c.dom.Node; 085 086 /** 087 * A <code>${type_name}</code> class.<br/> 088 * This listener does more than just search for data. It searches for data *and* then searches if there are services 089 * (WMS, WFS) available, which provide this data. 090 * 091 * @author <a href="mailto:mays@lat-lon.de">Judit Mays</a> 092 * @author last edited by: $Author: apoth $ 093 * 094 * @version $Revision: 27569 $, $Date: 2010-10-28 08:57:50 +0200 (Do, 28 Okt 2010) $ 095 */ 096 public class SimpleSearchListener extends AbstractListener { 097 098 private static final ILogger LOG = LoggerFactory.getLogger( SimpleSearchListener.class ); 099 100 /** 101 * used in jsp pages 102 */ 103 public static final String HTML_FRAGMENT = "HTML_FRAGMENT"; // needs to be public for jsp pages 104 105 static final String RESULT_SEARCH = "RESULT_SEARCH"; 106 107 static final String RPC_CATALOG = "catalog"; 108 109 static final String RPC_FORMAT = "RPC_FORMAT"; // ISO19115, ISO19119, ... 110 111 static final String SESSION_AVAILABLESERVICECATALOGS = "AVAILABLESERVICECATALOGS"; 112 113 static final String SESSION_DATARECORDS = "DATARECORDS"; 114 115 static final String SESSION_REQUESTFORRESULTS = "SESSION_REQUESTFORRESULTS"; 116 117 static final String SESSION_RESULTFORHITS = "SESSION_RESULTFORHITS"; 118 119 protected CSWClientConfiguration config = null; 120 121 // protected Node nsNode = null; 122 protected NamespaceContext nsContext = CommonNamespaces.getNamespaceContext(); 123 124 @Override 125 public void actionPerformed( FormEvent event ) { 126 127 RPCWebEvent rpcEvent = (RPCWebEvent) event; 128 HttpSession session = ( (HttpServletRequest) this.getRequest() ).getSession( true ); 129 config = (CSWClientConfiguration) session.getAttribute( Constants.CSW_CLIENT_CONFIGURATION ); 130 131 try { 132 validateRequest( rpcEvent ); 133 } catch ( Exception e ) { 134 gotoErrorPage( Messages.getMessage( "IGEO_STD_CSW_INVALID_RPC_REQ", e.getMessage() ) ); 135 LOG.logError( e.getMessage(), e ); 136 return; 137 } 138 139 List rpcCatalogs = null; 140 RPCStruct rpcStruct = null; 141 String rpcFormat = null; 142 String rpcProtocol = null; 143 144 try { 145 rpcCatalogs = extractRPCCatalogs( rpcEvent ); 146 rpcStruct = extractRPCStruct( rpcEvent, 1 ); 147 rpcFormat = (String) extractRPCMember( rpcStruct, RPC_FORMAT ); 148 LOG.logDebug( "rpcFormat: " + rpcFormat ); 149 rpcProtocol = (String) extractRPCMember( rpcStruct, Constants.RPC_PROTOCOL ); 150 LOG.logDebug( "rpcProtocol: " + rpcProtocol ); 151 } catch ( Exception e ) { 152 gotoErrorPage( Messages.getMessage( "IGEO_STD_CSW_INVALID_RPC_EVENT", e.getMessage() ) ); 153 LOG.logError( e.getMessage(), e ); 154 return; 155 } 156 157 // for further use in TurnPageListener 158 session.setAttribute( Constants.RPC_PROTOCOL, rpcProtocol ); 159 160 // first "GetRecords"-request (resultType="HITS", typeNames="dataset") 161 String req = null; 162 HashMap resultHits = null; 163 try { 164 req = createRequest( rpcStruct, rpcFormat, "HITS" ); 165 LOG.logDebug( "First GetRecords Request:\n", new XMLFragment( (Reader) new StringReader( req ), 166 XMLFragment.DEFAULT_URL ).getAsPrettyString() ); 167 } catch ( Exception e ) { 168 gotoErrorPage( Messages.getMessage( "IGEO_STD_CSW_INVALID_HITS_REQ", e.getMessage() ) ); 169 LOG.logError( e.getMessage(), e ); 170 return; 171 } 172 try { 173 resultHits = performRequest( rpcProtocol, req, rpcCatalogs ); 174 LOG.logDebug( "Result length for HITS is: ", resultHits.keySet().size() ); 175 // save to session for further use in TurnPageListener 176 session.setAttribute( SESSION_RESULTFORHITS, resultHits ); 177 } catch ( Exception e ) { 178 gotoErrorPage( Messages.getMessage( "IGEO_STD_CSW_SERVER_ERROR", e.getMessage() ) ); 179 LOG.logError( e.getMessage(), e ); 180 return; 181 } 182 183 int hits = 0; 184 try { 185 hits = numberOfMatchesInMap( resultHits ); 186 } catch ( Exception e ) { 187 gotoErrorPage( Messages.getMessage( "IGEO_STD_CSW_INVALID_RESULT", e.getMessage() ) ); 188 LOG.logError( e.getMessage(), e ); 189 return; 190 } 191 192 HashMap resultResults = null; 193 List dsrListSearch = null; 194 Map availableServiceCatalogsMap = null; 195 if ( hits > 0 ) { 196 197 // second "GetRecords"-request (resultType="RESULTS", typeNames="dataset") 198 req = null; 199 try { 200 req = createRequest( rpcStruct, rpcFormat, "RESULTS" ); 201 LOG.logDebug( "Second GetRecords Request :\n", req ); 202 // if resultType="RESULT", then save request to session to be able to perform 203 // the request again and again for more of the google-like pages (TurnPageListener) 204 session.setAttribute( SESSION_REQUESTFORRESULTS, req ); 205 } catch ( Exception e ) { 206 gotoErrorPage( Messages.getMessage( "IGEO_STD_CSW_INVALID_RESULTS_REQ", e.getMessage() ) ); 207 LOG.logError( e.getMessage(), e ); 208 return; 209 } 210 try { 211 resultResults = performRequest( rpcProtocol, req, rpcCatalogs ); 212 LOG.logDebug( "Result length for RESULTS is: ", resultResults.keySet().size() ); 213 } catch ( Exception e ) { 214 gotoErrorPage( Messages.getMessage( "IGEO_STD_CSW_SERVER_ERROR", e.getMessage() ) ); 215 LOG.logError( e.getMessage(), e ); 216 return; 217 } 218 219 // create data session records for results and store them in the session 220 try { 221 dsrListSearch = createDataSessionRecords( resultResults ); 222 LOG.logDebug( "dsrListSearch length=", dsrListSearch.size() ); 223 } catch ( CatalogClientException e ) { 224 gotoErrorPage( Messages.getMessage( "IGEO_STD_CSW_ERROR_CREATE_DSRLIST", e.getMessage() ) ); 225 LOG.logError( e.getMessage(), e ); 226 return; 227 } 228 229 try { 230 // TODO make usable for other formats, not only "ISO19119" 231 LOG.logDebug( "doService for ISO19119 HITS" ); 232 availableServiceCatalogsMap = doServiceSearch( resultResults, "ISO19119", "HITS" ); 233 } catch ( CatalogClientException e ) { 234 gotoErrorPage( Messages.getMessage( "IGEO_STD_CSW_ERROR_CREATE_SEARCH_RESULTS", e.getMessage() ) ); 235 LOG.logError( e.getMessage(), e ); 236 return; 237 } 238 } 239 // save List of data session records to session (may be null or empty) 240 session.setAttribute( SESSION_DATARECORDS, dsrListSearch ); 241 // save Map of available service catalogs to session (may be null or empty) 242 session.setAttribute( SESSION_AVAILABLESERVICECATALOGS, availableServiceCatalogsMap ); 243 244 // handle result: take result and transform it to produce html output 245 String fileName = "csw/metaList2html.xsl"; // default value 246 // FIXME replace format with current value 247 HashMap xslMap = config.getProfileXSL( "Profiles." + rpcFormat ); 248 if ( xslMap != null ) { 249 if ( xslMap.get( "brief" ) != null ) { 250 fileName = (String) xslMap.get( "brief" ); 251 } 252 } 253 254 try { 255 String pathToFile = "file:" + getHomePath() + "WEB-INF/conf/igeoportal/" + fileName; 256 LOG.logDebug( "path to file metList2Html: ", pathToFile ); 257 handleResult( resultHits, resultResults, pathToFile ); 258 } catch ( Exception e ) { 259 LOG.logError( e.getMessage(), e ); 260 gotoErrorPage( Messages.getMessage( "IGEO_STD_CSW_ERROR_HANDLE_RESULT", e.getMessage() ) ); 261 return; 262 } 263 264 return; 265 } 266 267 // /** 268 // * 269 // * @param session 270 // * @param dsrListSearch 271 // */ 272 // private void addDataSessionRecordsToSessionAttrib( HttpSession session, List dsrListSearch ) 273 // { 274 // 275 // 276 // List dsrListSession; 277 // if ( session.getAttribute( Constants.SESSION_DATARECORDS ) != null ) { 278 // dsrListSession = (ArrayList)session.getAttribute( Constants.SESSION_DATARECORDS ); 279 // for( int i = 0; i < dsrListSearch.size(); i++ ) { 280 // if ( ! dsrListSession.contains( dsrListSearch.get(i) ) ) { 281 // dsrListSession.add( dsrListSearch ); 282 // } 283 // } 284 // } else { 285 // dsrListSession = new ArrayList( dsrListSearch.size() ); 286 // dsrListSession.addAll( dsrListSearch ); 287 // } 288 // session.setAttribute( Constants.SESSION_DATARECORDS, dsrListSession ); 289 // 290 // 291 // } 292 293 /** 294 * Validates the rpc request and makes sure that all the needed parameters are included 295 * 296 * @param rpcEvent 297 * @throws CatalogClientException 298 */ 299 protected void validateRequest( RPCWebEvent rpcEvent ) 300 throws CatalogClientException { 301 302 RPCParameter[] params = extractRPCParameters( rpcEvent ); 303 304 // validity check for number of parameters in RPCMethodCall 305 if ( params.length != 2 ) { 306 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_WRONG_PARAMS_NUMBER", "2", 307 params.length ) ); 308 } 309 310 RPCStruct rpcStruct = extractRPCStruct( rpcEvent, 1 ); 311 String rpcFormat = (String) extractRPCMember( rpcStruct, RPC_FORMAT ); 312 String rpcProtocol = (String) extractRPCMember( rpcStruct, Constants.RPC_PROTOCOL ); 313 if ( rpcFormat == null || rpcProtocol == null ) { 314 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_ERROR_PARAMS_NOT_SET" ) ); 315 } 316 317 // go through each catalog of the rpc and validate 318 List rpcCatalogs = extractRPCCatalogs( rpcEvent ); 319 String rpc_catalog = null; 320 for ( int i = 0; i < rpcCatalogs.size(); i++ ) { 321 rpc_catalog = (String) rpcCatalogs.get( i ); 322 323 // validity check for catalog 324 String[] catalogs = config.getCatalogNames(); 325 boolean containsCatalog = false; 326 for ( int j = 0; j < catalogs.length; j++ ) { 327 if ( catalogs[j].equals( rpc_catalog ) ) { 328 containsCatalog = true; 329 } 330 } 331 if ( !containsCatalog ) { 332 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_WRONG_CAT", rpc_catalog ) ); 333 } 334 335 // validity check for format 336 // is requested catalog capable to serve requested metadata format? 337 List formats = config.getCatalogFormats( rpc_catalog ); 338 if ( formats == null || !formats.contains( rpcFormat ) ) { 339 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_WRONG_FORMAT", rpc_catalog, 340 rpcFormat ) ); 341 } 342 343 // validity check for protocol 344 // is requested catalog reachable through requested protocol? 345 List protocols = config.getCatalogProtocols( rpc_catalog ); 346 if ( !protocols.contains( rpcProtocol ) ) { 347 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_WRONG_PROTOCOL", rpc_catalog, 348 rpcProtocol ) ); 349 } 350 } 351 352 return; 353 } 354 355 /** 356 * This method creates a csw request with the RequestFactory of the passed format, using the paramter values passed 357 * in the rpcStruct. (csw:GetRecords request, if the passed resultType is HITS or RESULTS, and csw:GetRecordsById 358 * request, if the passed resultType is null). 359 * 360 * @param rpcStruct 361 * The struct contains parameter values to be used in the RequestFactory. 362 * @param format 363 * The format determines the RequestFactory to be used. 364 * @param resultType 365 * The type of desired result. Possible values are HITS or RESULTS or null. 366 * @return Returns the xml encoded request as <code>String</code>. 367 * @throws CatalogClientException 368 */ 369 protected String createRequest( RPCStruct rpcStruct, String format, String resultType ) 370 throws CatalogClientException { 371 372 CSWRequestFactory fac = RequestFactoryFinder.findFactory( format ); 373 fac.setConfiguration( config ); 374 String request = fac.createRequest( rpcStruct, resultType ); 375 376 return request; 377 } 378 379 /** 380 * Performs the CSW request and returns the result as a HashMap 381 * 382 * @param protocol 383 * @param request 384 * @param catalogs 385 * @return Returns a <code>HashMap</code>, which contains one key-value-pair for each catalogue, that has been 386 * searched. The key is the name of the catalogue. The value is the doc Document, that contains the number 387 * of matches (resultType="HITS"), or 1 to n metadata entries (resultType="RESULTS") 388 * @throws CatalogClientException 389 */ 390 protected HashMap performRequest( String protocol, String request, List catalogs ) 391 throws CatalogClientException { 392 393 HashMap<String, Document> result = new HashMap<String, Document>(); 394 395 // loop for all catalogues contained in catalogs 396 for ( int i = 0; i < catalogs.size(); i++ ) { 397 boolean useSOAP = false; 398 List list = config.getCatalogProtocols( (String) catalogs.get( i ) ); 399 LOG.logDebug( "Catalog List length=", list.size() ); 400 if ( protocol != null ) { 401 if ( !list.contains( protocol ) ) { 402 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_UNSUPPORTED_PROTOCOL" ) ); 403 } 404 useSOAP = "SOAP".equals( protocol ); 405 } else { 406 for ( int j = 0; j < list.size(); j++ ) { 407 if ( "SOAP".equals( list.get( j ) ) ) { 408 useSOAP = true; 409 break; 410 } 411 } 412 } 413 414 String cswAddress = config.getCatalogServerAddress( (String) catalogs.get( i ) ); 415 if ( cswAddress == null ) { 416 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_WRONG_SERVER_ADDR" ) ); 417 } 418 419 try { 420 if ( useSOAP ) { 421 // TODO test if this SOAP is working properly 422 StringBuffer soapRequest = new StringBuffer( 5000 ); 423 soapRequest.append( "<soap:Envelope " ); 424 soapRequest.append( "xmlns:soap=\"http://www.w3.org/2001/12/soap-envelope\" " ); 425 soapRequest.append( "soap:encodingStyle=\"http://www.w3.org/2001/12/soap-encoding\">" ); 426 soapRequest.append( request ).append( "</soap:Envelope>" ); 427 428 request = soapRequest.toString(); 429 } 430 431 // send post request 432 HttpClient httpclient = new HttpClient(); 433 LOG.logDebug( "CSW Address: " + cswAddress ); 434 httpclient = WebUtils.enableProxyUsage( httpclient, new URL( cswAddress ) ); 435 httpclient.getHttpConnectionManager().getParams().setSoTimeout( 30000 ); 436 PostMethod postMethod = new PostMethod( cswAddress ); 437 postMethod.setRequestEntity( new StringRequestEntity( request, "text/xml", 438 CharsetUtils.getSystemCharset() ) ); 439 httpclient.executeMethod( postMethod ); 440 // String resp = postMethod.getResponseBodyAsString(); 441 // Document doc = XMLTools.parse( new StringReader( resp ) ); 442 443 // Using XMLFragment instead of XMLTools to avoid encoding problems 444 XMLFragment frag = new XMLFragment(); 445 frag.load( postMethod.getResponseBodyAsStream(), cswAddress ); 446 Document doc = frag.getRootElement().getOwnerDocument(); 447 448 Element root = doc.getDocumentElement(); 449 if ( root.getLocalName().equals( "ExceptionReport" ) ) { 450 LOG.logError( "CSW Error\n " + new XMLFragment( root ).getAsPrettyString() ); 451 throw new CatalogClientException( extractException( root ) ); 452 } else if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 453 LOG.logDebug( "\nResponse: \n", new XMLFragment( root ).getAsPrettyString() ); 454 } 455 456 // write key-value-pair to HashMap 457 result.put( (String) catalogs.get( i ), doc ); 458 459 } catch ( Exception e ) { 460 LOG.logError( e.getMessage(), e ); 461 throw new CatalogClientException( e.getMessage() ); 462 } 463 } 464 return result; 465 } 466 467 /** 468 * Handles the result of the search requests and transforms the result to HTML using xslt, then saves the 469 * transformed result into the session 470 * 471 * @param resultHits 472 * @param resultResults 473 * @param pathToXslFile 474 * e.g. file://$iGeoPortal_home$/WEB-INF/conf/igeoportal/metaList2html.xsl 475 * @throws XMLParsingException 476 * if the documents contained in resultHits don't have the expected structure. 477 * @throws CatalogClientException 478 */ 479 protected void handleResult( Object resultHits, Object resultResults, String pathToXslFile ) 480 throws XMLParsingException, CatalogClientException { 481 482 Map map = ( resultResults != null ) ? (HashMap) resultResults : (HashMap) resultHits; 483 if ( resultResults != null ) { 484 LOG.logDebug( "Resultset Result will be used" ); 485 } else { 486 LOG.logDebug( "Resultset HITS will be used" ); 487 } 488 Iterator it = map.keySet().iterator(); 489 490 URL u = null; 491 StringBuffer htmlFragment = new StringBuffer( 5000 ); 492 MetadataTransformer mt = null; 493 try { 494 u = new URL( pathToXslFile ); 495 mt = new MetadataTransformer( u.getFile() ); 496 } catch ( Exception e ) { 497 throw new CatalogClientException( e.getMessage() ); 498 } 499 500 String catalog = null; 501 Document doc = null; 502 String docString = null; 503 // one loop for each catalog in result ( where resultType="RESULTS" ) 504 while ( it.hasNext() ) { 505 catalog = (String) it.next(); 506 doc = (Document) map.get( catalog ); 507 LOG.logDebug( "Document to transform to html: \n", 508 new XMLFragment( doc.getDocumentElement() ).getAsPrettyString() ); 509 docString = DOMPrinter.nodeToString( doc, CharsetUtils.getSystemCharset() ); 510 Reader reader = new StringReader( docString ); 511 512 // need to get numberOfRecordsMatched from the document where resultType is "HITS", 513 // because only there the number is correct. 514 // In document where resultType="RESULTS" the numberOfRecordsMatched is always equal to 515 // numberOfRecordReturned, which is LESS OR EQUAL to the correct numberOfRecordsMatched. 516 int matches = numberOfMatchesInDoc( (Document) ( (HashMap) resultHits ).get( catalog ) ); 517 518 // need to get startPosition from the request, because the value 'nextRec' in 519 // GetRecordsResponse is always '0', which is not always correct ;o) 520 int startPos = 0; 521 // load request from session 522 HttpSession session = ( (HttpServletRequest) this.getRequest() ).getSession( true ); 523 String req = (String) session.getAttribute( SESSION_REQUESTFORRESULTS ); 524 if ( req != null ) { 525 try { 526 StringReader sr = new StringReader( req ); 527 Document docReq = XMLTools.parse( sr ); 528 startPos = Integer.parseInt( docReq.getDocumentElement().getAttribute( "startPosition" ) ); 529 } catch ( Exception e ) { 530 throw new CatalogClientException( e.getMessage() ); 531 } 532 } 533 534 // transformation 535 try { 536 String transformedMetaData = mt.transformMetadata( reader, catalog, null, matches, startPos, "list" ); 537 htmlFragment.append( transformedMetaData ); 538 LOG.logDebug( "transformed html fragment:\n", htmlFragment.toString() ); 539 } catch ( Exception e ) { 540 throw new CatalogClientException( e.getMessage() ); 541 } 542 } 543 544 this.getRequest().setAttribute( RESULT_SEARCH, resultResults ); 545 this.getRequest().setAttribute( HTML_FRAGMENT, htmlFragment.toString() ); 546 547 } 548 549 /** 550 * Created a list of DataSessionRecord from all the returned results 551 * 552 * @param results 553 * @return Returns a List of distinct DataSessionRecords for all metadata elements within the passed results. 554 * @throws CatalogClientException 555 * if the identifier or the title of a metadata element could not be extracted. 556 */ 557 protected List<DataSessionRecord> createDataSessionRecords( HashMap results ) 558 throws CatalogClientException { 559 560 List<DataSessionRecord> dsrList = new ArrayList<DataSessionRecord>(); 561 562 Iterator it = results.keySet().iterator(); 563 while ( it.hasNext() ) { 564 String catalog = (String) it.next(); 565 Document doc = (Document) results.get( catalog ); 566 567 List mdList; // list of unique metadata element nodes 568 try { 569 mdList = extractMetadata( doc ); 570 } catch ( Exception e ) { 571 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_ERROR_EXTRACT_MD_ELEMS", 572 e.getMessage() ) ); 573 } 574 575 for ( int j = 0; j < mdList.size(); j++ ) { 576 Node mdNode = (Node) mdList.get( j ); 577 578 String xPathToId = getXPathToId( (Element) mdNode ); 579 String xPathToTitle = getXPathToTitle( (Element) mdNode ); 580 LOG.logDebug( "xPathToId=", xPathToId ); 581 LOG.logDebug( "xPathToDataTitle=", xPathToTitle ); 582 String id = null; 583 String title = null; 584 try { 585 id = extractValue( mdNode, xPathToId ); 586 LOG.logDebug( "extracted id=", id ); 587 title = extractValue( mdNode, xPathToTitle ); 588 LOG.logDebug( "extracted title=", title ); 589 } catch ( Exception e ) { 590 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_ERROR_EXTRACT_TITLE_IDENT", 591 e.getMessage() ) ); 592 } 593 DataSessionRecord dsr = new DataSessionRecord( id, catalog, title ); 594 595 if ( !dsrList.contains( dsr ) ) { 596 // this should be a redundant check. testing just in case... 597 dsrList.add( dsr ); 598 } 599 } 600 } 601 602 return dsrList; 603 } 604 605 /** 606 * Extracts all Metadata nodes from the passed csw:GetRecordsResponse Document. 607 * 608 * @param doc 609 * The csw:GetRecordsResponse Document from which to extract the Metadata nodes. 610 * @return Returns a NodeList of Metadata Elements for the passed Document. 611 * @throws CatalogClientException 612 * if metadata nodes could not be extracted from the passed Document. 613 * @throws XMLParsingException 614 */ 615 protected List extractMetadata( Document doc ) 616 throws CatalogClientException, XMLParsingException { 617 618 List nl = null; 619 620 String xPathToMetadata = "csw202:GetRecordsResponse/csw202:SearchResults/child::*"; 621 // nl = XMLTools.getXPath( xPathToMetadata, doc, nsNode ); // old 622 nl = XMLTools.getNodes( doc, xPathToMetadata, nsContext ); // new 623 if ( nl == null || nl.size() < 1 ) { 624 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_ERROR_EXTRACT_MD_NODES" ) ); 625 } 626 627 return nl; 628 } 629 630 /** 631 * Extracts node value 632 * 633 * @param node 634 * @param xPath 635 * @return Returns the value for the passed node and xPath. 636 * @throws CatalogClientException 637 * @throws XMLParsingException 638 */ 639 protected String extractValue( Node node, String xPath ) 640 throws CatalogClientException, XMLParsingException { 641 642 String s = XMLTools.getNodeAsString( node, xPath, nsContext, null ); 643 if ( s == null ) { 644 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_ERROR_NO_VALUE_FOUND", xPath ) ); 645 } 646 647 return s; 648 } 649 650 /** 651 * The number of matches returned is the number of matches for all catalogs added together. 652 * 653 * @param result 654 * The HashMap containing the result document from the performed request. 655 * @return Returns the number of matches indicated in the result. 656 * @throws XMLParsingException 657 * if the result document in the passed HashMap does not contain the expected nodes and attributes. 658 */ 659 private int numberOfMatchesInMap( HashMap result ) 660 throws XMLParsingException { 661 662 int hits = 0; 663 664 Iterator iterator = result.keySet().iterator(); 665 while ( iterator.hasNext() ) { 666 String catalog = (String) iterator.next(); 667 Document doc = (Document) result.get( catalog ); // result(value) 668 Element root = doc.getDocumentElement(); 669 if ( root.getLocalName().equals( "ExceptionReport" ) ) { 670 LOG.logError( "csw Error\n " + new XMLFragment( root ).getAsPrettyString() ); 671 throw new XMLParsingException( extractException( root ) ); 672 } 673 int matches = numberOfMatchesInDoc( doc ); 674 hits += matches; 675 } 676 677 return hits; 678 } 679 680 /** 681 * Extracts the exception message from a given ows:Excpetion xml fragment 682 * 683 * @param root 684 * 685 * @return the error message 686 * 687 * @throws XMLParsingException 688 */ 689 private String extractException( Element root ) 690 throws XMLParsingException { 691 return XMLTools.getRequiredNodeAsString( root, "./ows:Exception", nsContext ); 692 } 693 694 /** 695 * The number of matches returned is the number for one single catalog. 696 * 697 * @param doc 698 * The Document containing the result for one catalog. 699 * @return Returns the number of matches for one catalog only. 700 * @throws XMLParsingException 701 */ 702 private int numberOfMatchesInDoc( Document doc ) 703 throws XMLParsingException { 704 705 Element docElement = doc.getDocumentElement(); // root element 706 707 if ( docElement.getLocalName().equals( "ExceptionReport" ) ) { 708 LOG.logError( "csw Error\n " + new XMLFragment( docElement ).getAsPrettyString() ); 709 throw new XMLParsingException( extractException( docElement ) ); 710 } 711 Element searchResults = (Element) XMLTools.getRequiredNode( docElement, 712 StringTools.concat( 100, "./", 713 CommonNamespaces.CSW202_PREFIX, 714 ":SearchResults" ), nsContext ); 715 String matches = XMLTools.getRequiredAttrValue( "numberOfRecordsMatched", null, searchResults ); 716 717 return Integer.parseInt( matches ); 718 } 719 720 /** 721 * Invokes a GetRecords request with type RESULT to get the actual records for all the metadata returned during the 722 * GetRecords request with type HIT 723 * 724 * @param result 725 * HashMap containing data catalog names (as keys) and GetRecordResponse Documents (as values). 726 * @param format 727 * some service format like "ISO19119" 728 * @param resultType 729 * either "HITS" or "RESULTS". 730 * @return Returns a Map that contains the title extracted from the passed document (as key) and a List of all 731 * corresponding available service catalogs (as value). 732 * @throws CatalogClientException 733 */ 734 protected Map doServiceSearch( HashMap result, String format, String resultType ) 735 throws CatalogClientException { 736 737 List serviceCatalogs = config.getServiceMetadataCatalogs(); 738 Map<String, List<String>> availableServiceCatalogsMap = new HashMap<String, List<String>>( 10 ); 739 740 // for each kvp (i.e. for each data catalog) in result do the following loop 741 Iterator iterator = result.keySet().iterator(); 742 while ( iterator.hasNext() ) { 743 String catalog = (String) iterator.next(); // result(key) 744 Document doc = (Document) result.get( catalog ); // result(value) 745 746 List nl; // list of unique metadata element nodes 747 try { 748 nl = extractMetadata( doc ); 749 } catch ( Exception e ) { 750 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_ERROR_EXTRACT_METADATA", 751 e.getMessage() ) ); 752 } 753 List<String> idList = new ArrayList<String>( nl.size() ); 754 755 // csw: 2.0.0 756 /* 757 * if ( "full".equals( elementSet ) ) { xPathToTitle = config.getXPathToDataTitleFull(); } 758 */ 759 760 // get list of titles for current catalog 761 // REASON: the link between iso19115 and iso19119 is the "title". 762 // TODO make this work for other formats where the link is something else completely. 763 try { 764 for ( int i = 0; i < nl.size(); i++ ) { 765 idList.add( extractValue( (Node) nl.get( i ), getXPathToId( (Element) nl.get( i ) ) ) ); 766 } 767 } catch ( Exception e ) { 768 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_ERROR_EXTRACT_TITLE", 769 e.getMessage() ) ); 770 } 771 772 // for each title 773 for ( int i = 0; i < idList.size(); i++ ) { 774 // DO SERVICE SEARCH 775 // simple search had results (number of hits > 0), so a getRecords request with 776 // resulttype=RESULTS was done and now a service search is needed for those results. 777 778 // get the service info: search a service for the current title 779 RPCStruct serviceStruct = null; 780 String template = "CSWServiceSearchRPCMethodCallTemplate.xml"; 781 try { 782 serviceStruct = createRpcStructForServiceSearch( template, idList.get( i ) ); 783 } catch ( Exception e ) { 784 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_INVALID_STRUCT", 785 e.getMessage() ) ); 786 } 787 String serviceReq = null; 788 try { 789 serviceReq = createRequest( serviceStruct, format, resultType ); 790 LOG.logDebug( "created service request=\n", serviceReq ); 791 } catch ( Exception e ) { 792 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_INVALID_REQ", e.getMessage() ) ); 793 } 794 795 HashMap serviceResult = null; 796 try { 797 serviceResult = performRequest( null, serviceReq, serviceCatalogs ); 798 } catch ( Exception e ) { 799 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_SERVER_ERROR", e.getMessage() ) ); 800 } 801 List<String> availableServiceCatalogs = null; 802 // get service catalogs that are available for the current dataTitle 803 try { 804 availableServiceCatalogs = extractAvailableServiceCatalogs( serviceResult ); 805 } catch ( XMLParsingException e ) { 806 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_INVALID_RESULT", 807 e.getMessage() ) ); 808 } 809 availableServiceCatalogsMap.put( idList.get( i ), availableServiceCatalogs ); 810 } // end for each dataTitle 811 } // end for each kvp in result 812 813 return availableServiceCatalogsMap; 814 } 815 816 /** 817 * Extract all catalogs from serviceResult that actually do have the service available (= where number of records 818 * matched is greater than 0) and add them to the returned List. 819 * 820 * @param serviceResult 821 * @return Returns a <code>List</code> of all available service catalogs. May be null. 822 * @throws XMLParsingException 823 */ 824 private List<String> extractAvailableServiceCatalogs( Map serviceResult ) 825 throws XMLParsingException { 826 827 List<String> availableServiceCatalogs = new ArrayList<String>( serviceResult.size() ); 828 829 // one loop for each catalog in serviceResult 830 Iterator it = serviceResult.keySet().iterator(); 831 while ( it.hasNext() ) { 832 String servCatalog = (String) it.next(); 833 Document servDoc = (Document) serviceResult.get( servCatalog ); 834 835 int matches = numberOfMatchesInDoc( servDoc ); 836 if ( matches > 0 ) { 837 availableServiceCatalogs.add( servCatalog ); 838 } 839 } 840 841 return ( availableServiceCatalogs.size() == 0 ) ? null : availableServiceCatalogs; 842 } 843 844 /** 845 * Creates an RPC request from the given template 846 * 847 * @param template 848 * @param identifier 849 * @return Returns the new rpcStruct. 850 * @throws CatalogClientException 851 * @throws RPCException 852 */ 853 protected RPCStruct createRpcStructForServiceSearch( String template, String identifier ) 854 throws CatalogClientException, RPCException { 855 856 RPCStruct rpcStruct = null; 857 InputStream is = SimpleSearchListener.class.getResourceAsStream( template ); 858 859 String rpc = null; 860 try { 861 InputStreamReader ireader = new InputStreamReader( is ); 862 BufferedReader br = new BufferedReader( ireader ); 863 StringBuffer sb = new StringBuffer( 50000 ); 864 while ( ( rpc = br.readLine() ) != null ) { 865 sb.append( rpc ); 866 } 867 rpc = sb.toString(); 868 br.close(); 869 } catch ( Exception e ) { 870 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_TEMPLATE_ERROR", e.getMessage() ) ); 871 } 872 873 // replace templates in struct with passed values 874 rpc = rpc.replaceAll( "\\$SEARCH", identifier ); 875 876 StringReader reader = new StringReader( rpc ); 877 RPCMethodCall mc = RPCFactory.createRPCMethodCall( reader ); 878 try { 879 RPCParameter[] params = mc.getParameters(); 880 rpcStruct = (RPCStruct) params[0].getValue(); 881 } catch ( Exception e ) { 882 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_ERROR_EXTRACT_STRUCT_FROM_RPC", 883 e.getMessage() ) ); 884 } 885 886 return rpcStruct; 887 } 888 889 /** 890 * Extracts the parameters from the method call element within the passed rpcEvent. 891 * 892 * @param rpcEvent 893 * @return Returns the parameters as array of <code>RPCParameter</code>. 894 * @throws CatalogClientException 895 */ 896 protected RPCParameter[] extractRPCParameters( RPCWebEvent rpcEvent ) 897 throws CatalogClientException { 898 899 RPCParameter[] params; 900 try { 901 RPCMethodCall mc = rpcEvent.getRPCMethodCall(); 902 params = mc.getParameters(); 903 } catch ( Exception e ) { 904 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_ERROR_EXTRACT_PARAMS_FROM_RPC", 905 e.getMessage() ) ); 906 } 907 return params; 908 } 909 910 /** 911 * Extracts the catalog names from the first parameter of the params element within the passed rpcEvent. 912 * 913 * @param rpcEvent 914 * @return Returns the catalogue names as array of <code>String</code>. 915 * @throws CatalogClientException 916 */ 917 protected List extractRPCCatalogs( RPCWebEvent rpcEvent ) 918 throws CatalogClientException { 919 920 List<String> catalogs = new ArrayList<String>( 10 ); 921 try { 922 RPCParameter[] params = extractRPCParameters( rpcEvent ); 923 RPCParameter[] rpcCatalogs = (RPCParameter[]) params[0].getValue(); 924 for ( int i = 0; i < rpcCatalogs.length; i++ ) { 925 catalogs.add( (String) rpcCatalogs[i].getValue() ); 926 } 927 } catch ( Exception e ) { 928 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_ERROR_EXTRACT_CATNAME" ) ); 929 } 930 return catalogs; 931 } 932 933 /** 934 * Extracts the <code>RPCStruct</code> from the indicated parameter in the params element of the passed 935 * <code>RPCWebEvent</code>. 936 * 937 * @param rpcEvent 938 * The RPCWebEvent, that contains the RPCStruct to extract. 939 * @param index 940 * The index of the parameter from which to extract the RPCStruct (starting with 0). 941 * @return Returns the <code>RPCStruct</code> from the indicated params element. 942 * @throws CatalogClientException 943 */ 944 protected RPCStruct extractRPCStruct( RPCWebEvent rpcEvent, int index ) 945 throws CatalogClientException { 946 RPCStruct rpcStruct; 947 try { 948 RPCParameter[] params = extractRPCParameters( rpcEvent ); 949 rpcStruct = (RPCStruct) params[index].getValue(); 950 } catch ( Exception e ) { 951 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_ERROR_EXTRACT_STRUCT_FROM_RPC", 952 e.getMessage() ) ); 953 } 954 return rpcStruct; 955 } 956 957 /** 958 * Extracts the member of the passed name from the passed struct. 959 * 960 * @param struct 961 * The rpcStruct to extract the passed member from. 962 * @param member 963 * The Member to extract from the passed rpcStruct. 964 * @return Returns the member value object. 965 * @throws CatalogClientException 966 */ 967 protected Object extractRPCMember( RPCStruct struct, String member ) 968 throws CatalogClientException { 969 RPCMember rpcMember; 970 try { 971 rpcMember = struct.getMember( member ); 972 } catch ( Exception e ) { 973 throw new CatalogClientException( Messages.getMessage( "IGEO_STD_CSW_ERROR_EXTRACT_MEMBER", member, 974 e.getMessage() ) ); 975 } 976 return rpcMember.getValue(); 977 } 978 979 /** 980 * Checks if the metadata is of series, dataset, application or service type 981 * 982 * @param docElem 983 * @return "service": if the metadata is a service "dataset": if the metadata is a dataset "series" : if the 984 * metadata is a datasetCollection (series) 985 */ 986 protected String getMetadataType( Element docElem ) { 987 try { 988 return XMLTools.getAttrValue( XMLTools.getRequiredNode( docElem, "./gmd:hierarchyLevel/gmd:MD_ScopeCode", 989 nsContext ), null, "codeListValue", null ); 990 } catch ( Exception e ) { 991 return null; 992 } 993 } 994 995 /** 996 * Checks whether the metadata document is of a known type. The known types are "series", "dataset", "service" and 997 * "application" 998 * 999 * @param docElem 1000 * @return true if it is a known metadata type, false otherwise 1001 */ 1002 protected boolean isKnownMetadataType( Element docElem ) { 1003 String scopeCode = getMetadataType( docElem ); 1004 if ( "series".equals( scopeCode ) || "dataset".equals( scopeCode ) || "service".equals( scopeCode ) 1005 || "application".equals( scopeCode ) ) { 1006 return true; 1007 } 1008 return false; 1009 } 1010 1011 /** 1012 * @param docElem 1013 * @return xPath to the record identifier 1014 */ 1015 protected String getXPathToId( Element docElem ) { 1016 String mdType = getMetadataType( docElem ); 1017 if ( "series".equals( mdType ) || "dataset".equals( mdType ) || "application".equals( mdType ) ) { 1018 return config.getXPathToDataIdentifier(); 1019 } else if ( "service".equals( mdType ) ) { 1020 return config.getXPathToServiceIdentifier(); 1021 } 1022 return null; 1023 } 1024 1025 /** 1026 * @param docElement 1027 * @return xPath to record title 1028 */ 1029 protected String getXPathToTitle( Element docElement ) { 1030 String mdType = getMetadataType( docElement ); 1031 if ( "series".equals( mdType ) || "dataset".equals( mdType ) || "application".equals( mdType ) ) { 1032 return config.getXPathToDataTitle(); 1033 } else if ( "service".equals( mdType ) ) { 1034 return config.getXPathToServiceTitle(); 1035 } 1036 return null; 1037 } 1038 1039 // /** 1040 // * Extracts the member of the passed name from the passed struct. 1041 // * 1042 // * @param struct The rpcStruct to extract the passed member from. 1043 // * @param memberName The Member to extract from the passed rpcStruct. 1044 // * @return Returns the member value object. 1045 // * @throws CatalogClientException 1046 // */ 1047 // protected Object extractRPCMemberValue( RPCStruct struct, String memberName ) 1048 // throws CatalogClientException { 1049 // String memberValue = null; 1050 // try { 1051 // memberValue = (String) struct.getMember( memberName ).getValue(); 1052 // } catch (Exception e) { 1053 // throw new CatalogClientException( "Cannot extract member value "+ memberName +" from 1054 // RPCStruct: " 1055 // + e.getMessage() ); 1056 // } 1057 // return memberValue; 1058 // } 1059 1060 }