domingo, 26 de octubre de 2014

Comunicación Soap

Para la comunicación cliente/servidor, en el caso de las arquitecturas móviles se recomiendan protocolos ligeros como el caso de JSON que sobrecarga mínimamente la información transmitida, resulta independiente de la plataforma del servidor, y presenta muchas facilidades a la hora de parsear la información recibida como el empleo de la API Gson o de las propias clases Android de manejo de JSON como JSONObject.
De cualquier forma, en ocasiones resulta necesario acudir a web services clásicos que emplean el protocolo Soap para la comunicación, el cual transmite la información en formato XML que deberá ser procesado en el cliente al recibir la respuesta.

Compatibilidad

v1

Lo mínimo necesario

Es necesario permiso de Internet en el Manifest:
<uses-permission android:name="android.permission.INTERNET"/>

Una clase que represente el objeto del servidor:
public class Weather {
      
       private String location;
       private String time;
       private String wind;
       private String visibility;
       private String temperature;
       private String dewPoint;
       private String relativeHumidity;
       private String pressure;
       private String status;
      
       public String getLocation() {
             return location;
       }
       public void setLocation(String location) {
             this.location = location;
       }
       public String getTime() {
             return time;
       }
       public void setTime(String time) {
             this.time = time;
       }
       public String getWind() {
             return wind;
       }
       public void setWind(String wind) {
             this.wind = wind;
       }
       public String getVisibility() {
             return visibility;
       }
       public void setVisibility(String visibility) {
             this.visibility = visibility;
       }
       public String getTemperature() {
             return temperature;
       }
       public void setTemperature(String temperature) {
             this.temperature = temperature;
       }
       public String getDewPoint() {
             return dewPoint;
       }
       public void setDewPoint(String dewPoint) {
             this.dewPoint = dewPoint;
       }
       public String getRelativeHumidity() {
             return relativeHumidity;
       }
       public void setRelativeHumidity(String relativeHumidity) {
             this.relativeHumidity = relativeHumidity;
       }
       public String getPressure() {
             return pressure;
       }
       public void setPressure(String pressure) {
             this.pressure = pressure;
       }
       public String getStatus() {
             return status;
       }
       public void setStatus(String status) {
             this.status = status;
       }
      
}

Una clase para conectar con el servicio web a través de la conexión Soap empleando la API ksoap, que deberá estar configurada en el proyecto Android. Cuando los datos se reciben se procesan con las clases Android para el parseo XML:
public class EnviarSoap {
    //Parámetros de conexión
    private static final String NAMESPACE = "http://www.webserviceX.NET";
    private static final String URL =
"http://www.webservicex.net/globalweather.asmx";
    private static final String METHOD_NAME = "GetWeather";
    private static final String SOAP_ACTION =
"http://www.webserviceX.NET/GetWeather";
   
    //Parámetros del método
    private static final String PARAM_CITY = "CityName";
    private static final String PARAM_COUNTRY = "CountryName";
   
    //Etiquetas XML
    private static final String TAG_ROOT = "CurrentWeather";
    private static final String TAG_LOCATION = "Location";
    private static final String TAG_TIME = "Time";
    private static final String TAG_WIND = "Wind";
    private static final String TAG_VISIBILITY = "Visibility";
    private static final String TAG_TEMPERATURE = "Temperature";
    private static final String TAG_DEW = "DewPoint";
    private static final String TAG_HUMIDITY = "RelativeHumidity";
    private static final String TAG_PRESSURE = "Pressure";
    private static final String TAG_STATUS = "Status";
   
    private Context context;
    private Weather item;
      
    public EnviarSoap(Context context){
       this.context = context;
    }

    /**
     * Envío de solicitud HTTP
     * @return
     * @throws Exception
     */
    public Weather callService(String city, String country)
                                            throws Exception {
       Weather res = null;
       try {
             //Crear la petición con los parámetros
             SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);             
             request.addProperty(PARAM_CITY, city);
             request.addProperty(PARAM_COUNTRY, country);
                   
             //Envelope de tipo .NET
             SoapSerializationEnvelope envelope =
                    new SoapSerializationEnvelope(SoapEnvelope.VER11);                       
             envelope.dotNet = true;
             envelope.setOutputSoapObject(request);
                   
             //Llamada al servicio
             HttpTransportSE transporte = new HttpTransportSE(URL);                  
             transporte.call(SOAP_ACTION, envelope);
                   
             //Obtener respuesta y procesar
             SoapPrimitive response =
(SoapPrimitive) envelope.getResponse();
             res = this.procesar(response.toString());     
       }catch (Exception ex){
             ex.printStackTrace();
       }
            
       return res;
    }
      
    /**
     * Método para obtener la respuesta
     * @param is string de entrada con los datos a procesar
     * @return objeto procesado
     * @throws IOException error en la conexión
     * @throws SAXException
     */
    public Weather procesar(String in) throws IOException, SAXException{
       RootElement root = new RootElement(TAG_ROOT);
Element eLocation = root.getChild(TAG_LOCATION);
Element eTime = root.getChild(TAG_TIME);
Element eWind = root.getChild(TAG_WIND);
Element eVisibility = root.getChild(TAG_VISIBILITY);
Element eTemperature = root.getChild(TAG_TEMPERATURE);
Element eDew = root.getChild(TAG_DEW);
Element eHumidity = root.getChild(TAG_HUMIDITY);
Element ePressure = root.getChild(TAG_PRESSURE);
Element eStatus = root.getChild(TAG_STATUS);
       
//Al iniciar el elemento raíz se crea nueva instancia
root.setStartElementListener(new StartElementListener() {
             public void start(Attributes attrs) {
                    item = new Weather();
             }
});
       
//Leer cadenas al finalizar la etiqueta
eLocation.setEndTextElementListener(new EndTextElementListener() {
             public void end(String str) {
                    item.setLocation(str);
             }
});
eTime.setEndTextElementListener(new EndTextElementListener() {
             public void end(String str) {
                    item.setTime(str);
             }
});
eWind.setEndTextElementListener(new EndTextElementListener() {
             public void end(String str) {
                    item.setWind(str);
             }
});
eVisibility.setEndTextElementListener(new EndTextElementListener() {
             public void end(String str) {
                    item.setVisibility(str);
             }
});
eTemperature.setEndTextElementListener(new EndTextElementListener() {
             public void end(String str) {
                    item.setTemperature(str);
             }
});
eDew.setEndTextElementListener(new EndTextElementListener() {
             public void end(String str) {
                    item.setDewPoint(str);
             }
});
eHumidity.setEndTextElementListener(new EndTextElementListener() {
             public void end(String str) {
                    item.setRelativeHumidity(str);
             }
});
ePressure.setEndTextElementListener(new EndTextElementListener() {
             public void end(String str) {
                    item.setPressure(str);
             }
});
eStatus.setEndTextElementListener(new EndTextElementListener() {
             public void end(String str) {
                    item.setStatus(str);
             }
});
            
       //Parsear o
       Xml.parse(in, root.getContentHandler());
       return item;
    }
}

Invocar a la clase anterior desde una tarea asíncrona:
private class TareaEnvio extends AsyncTask<String, Integer, Weather> {

       private ProgressDialog dialog;
                   
       @Override
       protected void onPreExecute() {
             dialog = ProgressDialog.show(MainActivity.this,
                                  getString(R.string.app_name),
getString(R.string.buscando), true);
       }

       @Override
       protected Weather doInBackground(String... args) {
             Weather res = null;
             try {
                    EnviarSoap client = new EnviarSoap(MainActivity.this);
                    res = client.callService(args[0], args[1]);
             } catch (Exception ex) {
                    ex.printStackTrace();
                    res = null;
             }
             return res;
       }

       @Override
       protected void onPostExecute(Weather result) {
             dialog.dismiss();
                          
             TextView tv = (TextView) findViewById(R.id.tvResultado);
             tv.setText("Temperature: " + result.getTemperature() +
                           "\nHumidity:" + result.getRelativeHumidity());
       }
}


Referencias

http://developer.android.com/reference/android/sax/Element.html