Android Open Source - EDCA Get Capabilities






From Project

Back to project page EDCA.

License

The source code is released under:

GNU General Public License

If you think the Android project EDCA listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package se.lu.nateko.edca.svc;
/*  w  w w  . ja v a2s.c  om*/
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import se.lu.nateko.edca.BackboneSvc;
import se.lu.nateko.edca.R;
import android.database.Cursor;
import android.os.AsyncTask;
import android.util.Log;

/********************************COPYRIGHT***********************************
 * This file is part of the Emergency Data Collector for Android (EDCA).  *
 * Copyright  2013 Mattias Spngmyr.                    *
 *                                       *
 *********************************LICENSE************************************
 * EDCA is free software: you can redistribute it and/or modify it under  *
 * the terms of the GNU General Public License as published by the Free    *
 * Software Foundation, either version 3 of the License, or (at your    *
 * option) any later version.                        *
 *                                      *
 * EDCA is distributed in the hope that it will be useful, but WITHOUT ANY  *
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or    *
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License  *
 * for more details.                            *
 *                                      *
 * You should have received a copy of the GNU General Public License along  *
 * with EDCA. If not, see "http://www.gnu.org/licenses/".          *
 *                                       *
 * The latest source for this software can be accessed at          *
 * "github.org/mattiassp/edca".                        *
 *                                       *
 * EDCA also utilizes the JTS Topology Suite, Version 1.8 by Vivid      *
 * Solutions Inc. It is released under the Lesser General Public License  *
 * ("http://www.gnu.org/licenses/") and its source can be accessed at the  *
 * JTS Topology Suite website ("http://www.vividsolutions.com/jts").    *
 *                                       *
 * Android is a trademark of Google Inc. The Android source is released  *
 * under the Apache License 2.0                        *
 * ("http://www.apache.org/licenses/LICENSE-2.0") and can be accessed at  *
 * "http://source.android.com/".                      *
 *                                       *
 * For other enquiries, e-mail to: edca.contact@gmail.com          *
 *                                       *
 ****************************************************************************
 * A subclass of AsyncTask for making a GetCapabilities request to a    *
 * geospatial server on a background worker thread and publishing the    *
 * result, including what layers are available on the server, back to the  *
 * UI thread.                                *
 *                                       *
 * @author Mattias Spngmyr                          *
 * @version 0.47, 2013-08-05                        *
 *                                       *
 ****************************************************************************/
public class GetCapabilities extends AsyncTask<ServerConnection, Void, GetCapabilities> {
  /** The error tag for this ASyncTask. */
  public static final String TAG = "GetCapabilities";
  /** Constant defining the wait time before the GetCapabilities request times out. */
  private static final int TIME_OUT = 60;
  /** The server http address and request. */
  private URI mServerURI;
  /** The ServerConnection that the request is being made to. */
  private ServerConnection mServerConnection;
  /** Whether or not this request has received a response. */
  private boolean mHasResponse;
  /** Whether or not the response include a valid Capabilities element. */
  private boolean mHasCapabilitiesElements = false;
  
  /** A reference to the application's background Service, received in the constructor. */
  private BackboneSvc mService;
  /** The HttpClient to use for sending the request. */
  private HttpClient mHttpClient;
  /** ArrayList of the names of already stored (in the local SQLite database) layers. Checked when adding new layers in order to avoid duplicates. */
  private ArrayList<String> mStoredLayers = new ArrayList<String>();
  
  /**
   * Constructor that stores a reference to the BackboneSvc creating this object.
   * @param service The BackboneSvc whose reference to keep.
   */
  public GetCapabilities(BackboneSvc service) {
    Log.d(TAG, "GetCapabilities(BackboneSvc) called.");
    mService = service;
  }
  
  /**
   * Method run in a separate worker thread when GetCapabilities.execute() is called.
   * Takes the server info from the ServerConnection supplied and stores it as a URI.
   * @param srvs  An array of ServerConnection objects from which to form the URI. May only contain 1.
   */
  @Override
  protected GetCapabilities doInBackground(ServerConnection... srvs) {
    Log.d(TAG, "doInBackground(ServerConnection...) called.");
    
    mServerConnection = srvs[0]; // Stores the ServerConnection info.
    
    /* Try to form an URI from the supplied ServerConnection info. */
    String uriString = mServerConnection.getAddress()
          + "/wms?service=wms&version=1.1.0&request=GetCapabilities";
    try {
      mServerURI = new URI(uriString);
    } catch (URISyntaxException e) {
      Log.e(TAG, e.getMessage() + ": " + uriString);
    }
    
    try  { // Get or wait for exclusive access to the HttpClient.
      mHttpClient = mService.getHttpClient(); 
    } catch(InterruptedException e) { Log.e(TAG, "Thread " + Thread.currentThread().getId() + ": " + e.toString()); }
    
    mHasResponse = getCapabilitiesRequest(); // Make the GetCapabilities request to the server and record the success state.
    mService.unlockHttpClient(); // Release the lock on the HttpClient, allowing new connections to be made.
    Log.v(TAG, "GetCapabilities request succeeded: " + String.valueOf(mHasResponse));
    return this;
  }


  /**
   * Method called from within the worker thread of doInBackground() to
   * publish the progress to the UI thread.
   */
  @Override
  protected void onProgressUpdate(Void... voids) {
    Log.d(TAG, "onProgressUpdate(Void...) called.");
    // No progress update.
    super.onProgressUpdate();
  }


  /**
   * Method that receives the return from doInBackground() and uses it
   * to perform work on the UI thread.
   */
  @Override
  protected void onPostExecute(GetCapabilities result) {
    Log.d(TAG, "onPostExecute(GetCapabilities) called.");
    
    mService.stopAnimation(); // Stop the animation, showing that a web communicating thread is no longer active.
    
    if(result.mHasResponse) { // Connection succeeded.
      /* Update the database. */
      mService.setActiveServer(mServerConnection);
      mService.getSQLhelper().updateData(LocalSQLDBhelper.TABLE_SRV, mServerConnection.getID(), LocalSQLDBhelper.KEY_SRV_ID, new String[] {LocalSQLDBhelper.KEY_SRV_LASTUSE}, new String[] {mServerConnection.getLastUse()});
      mService.setConnectState(BackboneSvc.CONNECTED, mService.getConnectingRow()); // Set the state to Connected, to the row that was being connected to.
    }
    else if(mService.getActiveServer() == null || mService.getActiveServer().getID() == mService.getConnectingRow()) // Connection failed with no connection present or while connecting to the currently active server.
      mService.clearConnection(true); // Report the failed connection attempt.
    else { // Connection failed while connecting to a new server, keep old connection.
      mService.setConnectState(BackboneSvc.CONNECTED, mService.getActiveServer().getID());
      mService.showAlertDialog(mService.getString(R.string.service_connectionfailed), null);
    }
    mService.updateLayoutOnState(); // Update the layout to reflect the results of the GetCapabilities request.

    mService.renewActiveLayer(mService.mInitialRenewLayer);
    
    super.onPostExecute(result);
  }
  
  /**
   * Method that calls a geospatial server using a GetCapablities request and,
   * if successful, forwards the resulting XML object to the XML parser.
   * @return  Returns true if successful, otherwise false.
   */
  protected boolean getCapabilitiesRequest() {
    Log.d(TAG, "getCapabilitiesRequest() called.");
    
      /* Execute the HTTP request. */
    HttpGet httpGetMethod = new HttpGet(mServerURI);      
      HttpResponse response;
    try {
      final HttpParams httpParameters = mHttpClient.getParams();
        HttpConnectionParams.setConnectionTimeout(httpParameters, TIME_OUT * 1000);
        HttpConnectionParams.setSoTimeout        (httpParameters, TIME_OUT * 1000);
        
        response = mHttpClient.execute(httpGetMethod);
        Log.i(TAG, "GetCapabilities request made to server: " + httpGetMethod.getURI().toString());
      
//        Log.v(TAG, "Length: " + response.getEntity().getContentLength() + ", Type: " + response.getEntity().getContentType() + ", Encoding: " + response.getEntity().getContentEncoding());

        InputStream xmlStream = response.getEntity().getContent();
        InputStreamReader reader = new InputStreamReader(xmlStream, "UTF-8");
        BufferedReader buffReader = new BufferedReader(reader, 4096);
        
        /* Remove all non-stored and inactive layers from the table to make room for the new result of the GetCapabilities XML parsing. */
        mService.clearRemoteLayers();
        
        /* Check for already stored layers so they are not duplicated. */
        Cursor storedCursor = mService.getSQLhelper().fetchData(LocalSQLDBhelper.TABLE_LAYER, LocalSQLDBhelper.KEY_LAYER_COLUMNS, LocalSQLDBhelper.ALL_RECORDS, null, true);
        mService.getActiveActivity().startManagingCursor(storedCursor);
        while(storedCursor.moveToNext()) {
          mStoredLayers.add(storedCursor.getString(1));
          Log.v(TAG, "Stored layer: " + storedCursor.getString(1));
        }
        
        try {
          Log.v(TAG, "Sending response (BufferedReader) to parser...");
          return parseXMLResponse(buffReader); // Send the HttpResponse as a Reader to parse its content.
        } finally {
          buffReader.close();
        }
          
    } catch (MalformedURLException e) {
      Log.e(TAG, e.toString());
      return false;
    } catch (IOException e) {
      Log.e(TAG, e.toString());
      return false;
    }
  }
  
  /**
   * Parses an XML response from a GetCapabilities request and stores available Layers
   * and options in the local SQLite database.
   * @param xmlResponse A reader wrapped around an InputStream containing the XML response from a GetCapabilities request.
   */
  protected boolean parseXMLResponse(BufferedReader xmlResponse) {
    Log.d(TAG, "parseXMLResponse(Reader) called.");

    try {
      SAXParserFactory spfactory = SAXParserFactory.newInstance(); // Make a SAXParser factory.
      spfactory.setValidating(false); // Tell the factory not to make validating parsers.
      SAXParser saxParser = spfactory.newSAXParser(); // Use the factory to make a SAXParser.
      XMLReader xmlReader = saxParser.getXMLReader(); // Get an XML reader from the parser, which will send event calls to its specified event handler.
      XMLEventHandler xmlEventHandler = new XMLEventHandler();
      xmlReader.setContentHandler(xmlEventHandler); // Set which event handler to use.
      xmlReader.setErrorHandler(xmlEventHandler); // Also set where to send error calls.
      InputSource source = new InputSource(xmlResponse); // Make an InputSource from the XML input to give to the reader.
        xmlReader.parse(source); // Start parsing the XML.
    } catch (Exception e) {
      Log.e(TAG, "XML parsing error: " + e.toString() + " - " + e.getMessage());
      return false;
    }
    return true;
  }
  
  /**
   * Event handler class that handles calls from an XML reader.
   * @author Mattias Spngmyr
   * @version 0.20, 2013-01-09
   */
  private class XMLEventHandler extends DefaultHandler {
    /** The error tag for this XMLEventHandler. */
    public static final String TAG = "GetCapabilities.XMLEventHandler";
    
    /** Whether or not the parser is within a <LAYER></LAYER> block. */
    private boolean mWithinLayerParent = false;
    /** Whether or not the parser is within two (inside a nested) <LAYER></LAYER> block. */
    private boolean mWithinLayerChild = false;
    /** The number of the level where inner <LAYER></LAYER> blocks are located. */
    private int mLayerChildLevel = 0;
    /** The number of the level of the element that is currently being parsed. */
    private int mElementLevel = 0;
    /** The type of element that is currently being parsed. */
    private int mElementType = 0;
    /** Whether or not the currently parsed LAYER element is queryable. */
    private boolean mLayerChildQueryable = false;
    /** The value of the element just parsed. */
    private String mElementValue = "";
    
    /** Flag recording whether or not the currently examined layer name is already present
     * in the layer table of the local SQLite database (first position) and whether or not
     * any layer name has been present (second position). */
    private boolean[] mConflict = new boolean[] { false, false };
    
    /** Constant defining the name of a "layer" element. */
    private static final String ELEMENT_KEY_LAYER = "layer";
    /** Constant defining the name of a "name" element. */
    private static final String ELEMENT_KEY_NAME = "name";
    /** Constant defining the name of an "srs" element. */
    private static final String ELEMENT_KEY_SRS = "SRS";
    /** Constant defining the name of a "service" element. */
    private static final String ELEMENT_KEY_SERVICE = "service";
    
    /** Constant identifying a "layer" element. */
    private static final int ELEMENT_MAPPING_LAYER = 1;
    /** Constant identifying a "name" element. */
    private static final int ELEMENT_MAPPING_NAME = 2;
    /** Constant identifying an "srs" element. */
    private static final int ELEMENT_MAPPING_SRS = 3;
    /** Constant identifying a "service" element. */
    private static final int ELEMENT_MAPPING_SERVICE = 4;
    /** Constant identifying elements other than those with specific identifiers. */
    private static final int ELEMENT_MAPPING_DEFAULT = 0;
    
    // TODO Handle coordinate reference system.
    //private boolean mServiceHasWGS84 = false; 
    
    @Override
    public void startDocument() throws SAXException {
      Log.d(TAG, "startDocument() called: Examining server capabilities...");
      super.startDocument();
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
//      Log.d(TAG, "startElement(String, localName=" + localName + ", String, Attributes) called.");
      mElementLevel++; // New element entered: the level in increased by one.
      
      /* Record relevant element names as integer mappings. */
      mElementType = (localName.equalsIgnoreCase(ELEMENT_KEY_LAYER))  ?  ELEMENT_MAPPING_LAYER  :  // Map LAYER as 1.
              (localName.equalsIgnoreCase(ELEMENT_KEY_NAME))   ?  ELEMENT_MAPPING_NAME  :  // Map NAME as 2.
              (localName.equalsIgnoreCase(ELEMENT_KEY_SRS))  ?  ELEMENT_MAPPING_SRS    :  // Map SRS as 3.
            (localName.equalsIgnoreCase(ELEMENT_KEY_SERVICE))  ?  ELEMENT_MAPPING_SERVICE  :  // Map SERVICE as 4.
                                        ELEMENT_MAPPING_DEFAULT;  // Map any other as 0.
      
      if(mElementType > 0) // Record if proper element has been found.
        mHasCapabilitiesElements = true;
      
      switch(mElementType) {
        case ELEMENT_MAPPING_LAYER: {
          if(mWithinLayerParent == false) { // If the element is LAYER, but the parent LAYER has not been read before.
            mWithinLayerParent = true; // Set the parent Layer to read.
          }
          else { // If the element is LAYER and the parent LAYER has been read.
            mLayerChildLevel = mElementLevel; // This is the level of child LAYERs.
            mWithinLayerChild = true;
            mLayerChildQueryable = (attributes.getValue("queryable").contentEquals("1")) ? true : false; // Record if the child LAYER is queryable.
          }
          break;
        }
        case ELEMENT_MAPPING_SERVICE: {
          // TODO
          break;
        }
        default:
          break;
      }      
      super.startElement(uri, localName, qName, attributes);
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
//      Log.d(TAG, "characters(char[], int, int) called.");
      /* Store the character data for relevant element types. */
      switch (mElementType) {
        case ELEMENT_MAPPING_DEFAULT:
          break;
        default: {          
          mElementValue = new String(ch, start, length); // Store the value of the layer element, including namespace.
          break;
        }
      }      
      super.characters(ch, start, length);
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
//      Log.d(TAG, "endElement(String, String, String) called.");
      switch(mElementType) {
        case ELEMENT_MAPPING_LAYER: { // Record that a LAYER element (Parent/Child) has ended.
          if(mWithinLayerChild)
            mWithinLayerChild = false;
          else
            mWithinLayerParent = false;
          break;
        }
        case ELEMENT_MAPPING_NAME: { // Log LAYER name and if queryable; store it in the local SQLite database.
          if(mElementLevel != (mLayerChildLevel + 1)) // If the element is not the child LAYER's NAME.
            break;
          if(mLayerChildQueryable) {
            for(int i=0; i < mStoredLayers.size(); i++) {
              if(mElementValue.equalsIgnoreCase(mStoredLayers.get(i))) { // Check for conflicts.
                mConflict[0] = true;
                mConflict[1] = true;
              }
            }
            
            if(mConflict[0]) { // There is a stored layer with the same name as this new one. Notify.              
              Log.i(TAG, "Layer not added: " + mElementValue);
              mConflict[0] = false; // Reset the conflict flag for the next layer check.
            }
            else {
              mService.getSQLhelper().insertData(LocalSQLDBhelper.TABLE_LAYER, new String[] {LocalSQLDBhelper.KEY_LAYER_NAME, LocalSQLDBhelper.KEY_LAYER_USEMODE}, new String[] {mElementValue, String.valueOf(LocalSQLDBhelper.LAYER_MODE_INACTIVE)});
              Log.i(TAG, "Layer added: " + mElementValue);
            }
          }
          else
            Log.w(TAG, "Non-queryable LAYER: " + mElementValue);
          break;
        }
        /*
        case 3: { // Record if the service has WGS 1984 as a coordinate system.
          if(!mWithinLayerChild)
            mServiceHasWGS84 = true;
        }
        */
        default:
          break;
      }
      
      mElementLevel--; // An element was left, the level is decreased by one.

      super.endElement(uri, localName, qName);
    }

    @Override
    public void endDocument() throws SAXException {
      Log.d(TAG, "endDocument() called: Finished examining server capabilities.");
      if(mConflict[1]) // If any layer was not added because its name was already stored locally, notify the user.
        mService.showAlertDialog(mService.getString(R.string.service_layerconflict), null);
      if(!mHasCapabilitiesElements)
        throw new SAXException("No valid elements found!");
      super.endDocument();
    }

    
    
    @Override
    public void warning(SAXParseException e) throws SAXException {
      Log.w(TAG, "warning(SAXParseException) called: " + e.toString());
      super.warning(e);
    }

    @Override
    public void error(SAXParseException e) throws SAXException {
      Log.e(TAG, "error(SAXParseException) called: " + e.toString());
      super.error(e);
    }

    @Override
    public void fatalError(SAXParseException e) throws SAXException {
      Log.e(TAG, "fatalError(SAXParseException) called: " + e.toString());
      super.fatalError(e);
    }
    
    

  }
}




Java Source Code List

se.lu.nateko.edca.About.java
se.lu.nateko.edca.AttributeEditor.java
se.lu.nateko.edca.BackboneSvc.java
se.lu.nateko.edca.LayerViewer.java
se.lu.nateko.edca.MainMenu.java
se.lu.nateko.edca.MapViewer.java
se.lu.nateko.edca.ServerEditor.java
se.lu.nateko.edca.ServerViewer.java
se.lu.nateko.edca.Utilities.java
se.lu.nateko.edca.svc.DescribeFeatureType.java
se.lu.nateko.edca.svc.GeoHelper.java
se.lu.nateko.edca.svc.GeographyLayer.java
se.lu.nateko.edca.svc.GetCapabilities.java
se.lu.nateko.edca.svc.GetMap.java
se.lu.nateko.edca.svc.LocalSQLDBhelper.java
se.lu.nateko.edca.svc.ServerConnection.java
se.lu.nateko.edca.svc.UnclosableBufferedInputStream.java