/******************************************************************************* * Copyright 2013 Geoscience Australia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package au.gov.ga.earthsci.discovery.csw; import gov.nasa.worldwind.util.WWXML; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import au.gov.ga.earthsci.core.retrieve.IRetrieval; import au.gov.ga.earthsci.core.retrieve.IRetrievalListener; import au.gov.ga.earthsci.core.retrieve.RetrievalAdapter; import au.gov.ga.earthsci.core.retrieve.RetrievalServiceFactory; import au.gov.ga.earthsci.core.retrieve.retriever.HttpRetrievalProperties; import au.gov.ga.earthsci.discovery.AbstractDiscovery; import au.gov.ga.earthsci.discovery.DiscoveryIndexOutOfBoundsException; import au.gov.ga.earthsci.discovery.DiscoveryResultNotFoundException; import au.gov.ga.earthsci.discovery.IDiscovery; import au.gov.ga.earthsci.discovery.IDiscoveryParameters; import au.gov.ga.earthsci.discovery.IDiscoveryResultLabelProvider; /** * {@link IDiscovery} implementation for a CSW service. * * @author Michael de Hoog (michael.dehoog@ga.gov.au) */ public class CSWDiscovery extends AbstractDiscovery<CSWDiscoveryService, IDiscoveryParameters> { private final IDiscoveryResultLabelProvider labelProvider = new CSWDiscoveryResultLabelProvider(); private final Map<Integer, CSWDiscoveryResult> results = new HashMap<Integer, CSWDiscoveryResult>(); private final Map<IRetrieval, String> retrievals = new HashMap<IRetrieval, String>(); private final Map<IRetrieval, Integer> retrievalStarts = new HashMap<IRetrieval, Integer>(); private final Set<String> retrievalIds = new HashSet<String>(); public CSWDiscovery(CSWDiscoveryService service, IDiscoveryParameters parameters) { super(service, parameters); } @Override public IDiscoveryResultLabelProvider getLabelProvider() { return labelProvider; } @Override public void start() { retrieve(0, 0); } @Override public void cancel() { synchronized (retrievals) { for (IRetrieval retrieval : retrievals.keySet()) { retrieval.cancel(); } retrievals.clear(); retrievalIds.clear(); retrievalStarts.clear(); } } @Override public CSWDiscoveryResult getResult(int index) throws DiscoveryResultNotFoundException, DiscoveryIndexOutOfBoundsException { synchronized (retrievals) { if (index < 0 || (resultCount != null && index >= resultCount)) { throw new DiscoveryIndexOutOfBoundsException(); } if (results.containsKey(index)) { return results.get(index); } int pageSize = getPageSize(); int start = (index / pageSize) * pageSize; retrieve(start, pageSize); return null; } } @Override public boolean supportsCustomPageSize() { return true; } protected void retrieve(int start, int length) { synchronized (retrievals) { String id = start + "_" + length; //$NON-NLS-1$ if (retrievalIds.contains(id)) { //already retrieving this range return; } HttpRetrievalProperties retrievalProperties = new HttpRetrievalProperties(); retrievalProperties.setRequestMethod("POST"); //$NON-NLS-1$ retrievalProperties.setUseCache(false); retrievalProperties.setContentType("text/xml"); //$NON-NLS-1$ CSWRequestParameters requestParameters = new CSWRequestParameters(); requestParameters.any = parameters.getQuery(); String query = service.getFormat().generateRequest(requestParameters, start + 1, length); try { byte[] payload = query.getBytes("UTF-8"); //$NON-NLS-1$ retrievalProperties.setRequestPayload(payload); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } IRetrieval retrieval = RetrievalServiceFactory.getServiceInstance().retrieve(this, service.getServiceURL(), retrievalProperties, true); retrievalIds.add(id); retrievals.put(retrieval, id); retrievalStarts.put(retrieval, start); retrieval.addListener(retrievalListener); retrieval.start(); loading = true; } } private IRetrievalListener retrievalListener = new RetrievalAdapter() { @Override public void complete(IRetrieval retrieval) { synchronized (retrievals) { String id = retrievals.remove(retrieval); if (id == null) { return; } retrievalIds.remove(id); int startIndex = retrievalStarts.remove(retrieval); loading = !retrievals.isEmpty(); if (retrieval.getResult().isSuccessful()) { InputStream is = null; try { is = retrieval.getData().getInputStream(); String transformed = service.getFormat().transformResponse(is); if (transformed != null) { DocumentBuilder builder = WWXML.createDocumentBuilder(false); Document document = builder.parse(new InputSource(new StringReader(transformed))); XPath xpath = WWXML.makeXPath(); Double totalRecordCount = (Double) xpath.compile("/GetRecordsResponse/numberOfRecordsMatched") //$NON-NLS-1$ .evaluate(document, XPathConstants.NUMBER); Double numberOfRecordsReturned = (Double) xpath.compile("/GetRecordsResponse/numberOfRecordsReturned") //$NON-NLS-1$ .evaluate(document, XPathConstants.NUMBER); if (!Double.isNaN(totalRecordCount)) { int newResultCount = totalRecordCount.intValue(); if (resultCount == null || resultCount != newResultCount) { resultCount = newResultCount; listeners.resultCountChanged(CSWDiscovery.this); } } if (!Double.isNaN(numberOfRecordsReturned) && numberOfRecordsReturned > 0) { int count = numberOfRecordsReturned.intValue(); NodeList recordElements = (NodeList) xpath.compile("/GetRecordsResponse/Record").evaluate( //$NON-NLS-1$ document, XPathConstants.NODESET); if (recordElements.getLength() != count) { throw new Exception( "Number of record elements in the CSW response doesn't match the number of records attribute"); //$NON-NLS-1$ } for (int i = 0; i < count; i++) { Element recordElement = (Element) recordElements.item(i); int index = startIndex + i; CSWDiscoveryResult result = new CSWDiscoveryResult(CSWDiscovery.this, index, recordElement); results.put(index, result); listeners.resultAdded(CSWDiscovery.this, result); } } } } catch (Exception e) { error = e; e.printStackTrace(); } finally { if (is != null) { try { is.close(); } catch (IOException e) { } } } } else if (error == null) { error = retrieval.getResult().getError(); } } } }; }