Olá pessoALL!
Os últimos artigos da série Android+kSOAP, escritos por Bruno Silva, introduziram o conceito de integração entre aplicações android com web services, através de parses dinâmicos, cache e modularização/encapsulamento de elementos dentro de um conceito EAI (Enterprise Application Integration).
Este artigo tem como objetivo a criação dinâmica de serviços sem a implementação do mesmo. Estranho, não é? Não é estranho até adicionarmos o conceito AOP (Aspect Oriented Programming) à solução de integração.
Como o foco deste artigo não é AOP, fica como sugestão a leitura do artigo AOP com Spring.NET. Este artigo cobre o conceito de AOP, no qual estaremos mencionando nos próximos tópicos.
AOP
AOP é um conceito simples, porém com alguns níveis de implementação:
- Linguagem de Programação: utilizar uma linguagem de programação que permite a construção de aspectos e acesso a recursos em tempo de execução e troca de mensagens entre os objetos em uma máquina virtual (por exemplo: Java Virtual Machine).
- Proxy: utilizar o mecanismo de Proxy de uma linguagem de programação para criar objetos dinamicamente com atuação de um controle (handler) nas chamadas de métodos destes objetos.
O nível de implementação que vamos utilizar neste artigo é Proxy. Vamos utilizar a classe:
java.lang.reflect.Proxy
Construindo Web Service
Antes de começar a criação dos serviços do lado da aplicação android, vamos criar o web service que realizará os processamentos de nossas chamadas.
Vou criar um serviço simples, utilizando a tecnologia .NET WCF (Windows Communication Foundation):
[ServiceContract]
public interface ISimpleService
{
[OperationContract]
string ProcessData(int value);
[OperationContract]
string GetData();
}
A implementação do serviço será:
public class SimpleService : ISimpleService
{
public string ProcessData(int value)
{
return string.Format("You entered: {0}", value);
}
public string GetData()
{
return "Some Data!";
}
}
O serviço será publicado locamente para acesso da aplicação android.
Acessando Serviços
Com o web service SimpleService publicado, vamos criar a solução para acesso aos seus dados, levando em consideração o conceito AOP.
A primeira parte será criar anotações para permitir o armazenamento de informações metadata do serviço que iremos acessar. Como assim? Criaremos uma anotação para o serviço e outra para as operações do serviço, respectivamente:
@Target (ElementType.TYPE )
@Retention (RetentionPolicy.RUNTIME)
public @interface SOAPService {
String serverUrl() default "";
String namespace() default "http://tempuri.org";
String serviceName();
String serviceInterface() default "";
}
Vejam, que esta anotação permite as seguintes informações em nível de serviço:
- serverURL: endereço do servidor que está publicado o web service.
- namespace: nome do contexto que está publicado o serviço.
- serviceName: nome do serviço publicado.
- serviceInterface: nome da interface do serviço publicado.
@Target (ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SOAPServiceOperation {
String name() default "";
Class parser();
String[] parameterNames() default {};
}
Já, esta anotação, permite informações em nível de operação de serviço:
- name: nome da operação.
- parser: implementação da interface IMethodResponseParser (veja o artigo Android+kSOAP – Pt.3).
- parameterNames: conjunto de nomes que simboliza os paramêtros a serem enviados, como argumentos, na operação.
Com as anotações criadas, vamos criar a interface do serviço que acessará o web service:
@SOAPService(namespace="http://tempuri.org",
serverUrl="http://localhost:51827",
serviceInterface="ISimpleService",
serviceName="SimpleService")
public interface DummyService {
@SOAPServiceOperation(name="GetData", parser=DummyServiceParser.class)
String getData();
}
Pronto! Criamos uma interface que será utilizada para criar serviços, de forma dinâmica, baseados no WSDL do web service.
Notem, que a classe de conversão – DummyServiceParser – é uma implementação da interface, já conhecida, dos artigos anteriores, IMethodResponseParser.
public class DummyServiceParser implements IMethodResponseParser {
@Override
public Object parse(SoapObject soapObject, SoapMapper soapMapper) {
return soapMapper.parseSoapObjectIntoAnnotatedVO(DummyObject.class, soapObject);
}
}
Agora, precisamos construir o mecanismo para gerar serviços, via Proxy, incluindo um controle de chamadas para as invocações de metódos destes objetos. Este mecanismo será uma factory – ServiceFactory – que criará Proxy com o seguinte controle de chamadas – ServiceInvocationHandler.
public final class ServiceFactory {
private static final ServiceFactory instance = new ServiceFactory();
private final <strong>ServiceInvocationHandler</strong> invocationHandler;
private ServiceFactory() {
invocationHandler = new ServiceInvocationHandler();
}
public static ServiceFactory getInstance() {
return instance;
}
public Object newService(Class<?> serviceInterfaceType) throws MalformedURLException {
if (!serviceInterfaceType.isInterface()) {
throw new IllegalArgumentException("Invalid type argument. Proxy service instance are created based on interface types.");
}
final ServiceInterfaceData serviceData = SoapAnnotationDataCache.addServiceInterface(serviceInterfaceType);
final URL url = serviceData.getServerUrl();
if (url == null) {
throw new MalformedURLException("Invalid server URL. Make sure that you set the [serverUrl] attribute from [" + SOAPService.class.getSimpleName() + "] annotation.");
}
return Proxy.newProxyInstance(ServiceFactory.class.getClassLoader(), new Class<?>[] { serviceInterfaceType }, this.invocationHandler);
}
}
Percebam, que a implementação do método ServiceFactory.newInstance utiliza a solução de cache do artigo Android+kSOAP – Pt.1. É anexado ao Proxy o controle de chamadas ServiceInvocationHandler.
public final class ServiceInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final ServiceInterfaceData serviceData = SoapAnnotationDataCache.getServiceInterfaceData(method);
final ServiceMethodData methodData = serviceData.getMethodData(method);
final String[] parameterNames = methodData.getParameterNames();
final LinkedHashMap<String, Object> params = new LinkedHashMap<String, Object>();
if (args != null && args.length != parameterNames.length) {
throw new IllegalArgumentException("The parameter names array length is different then invocation method arguments.");
}
if (args != null && parameterNames.length > 0) {
for (int i = 0; i < args.length; ++i) {
params.put(parameterNames[i], args[i]);
}
}
return new Caller<Object>()
.setParams(params)
.setServerUrl(serviceData.getServerUrl())
.setNamespace(serviceData.getNamespace())
.setService(serviceData.getServiceName())
.setServiceInterface(serviceData.getServiceInterface())
.setMethod(methodData.getName())
.setResponseParser(methodData.getParser()).call();
}
}
Mais uma vez, estou utilizando um elemento já conhecido, na série de artigos Android+kSOAP, o elemento Caller. Portanto, toda chamada realizada no Proxy, será direcionada ao controle de chamadas, que executará o acesso ao web service. O resultado da operação será convertido pela classe DummyServiceParser, anotada na operação do serviço.
Conclusão
Simplificar acesso a web service ou a recursos externos, utilizando conceitos de EAI, é primordial para evolução de softwares que necessitam deste tipo de solução.
Vejam, que no decorrer do artigo não mencionei sobre conversão e transformação automática dos dados enviados e retornados. Toda conversão é realizada por um parser, o que poderia ser um processo transparente para a utilização desta solução.
Uma solução open-source é a utilização da biblioteca ASoap. Esta biblioteca permite acesso de forma transparente, juntamente com a conversão e transformação de dados de um modo simples e flexível. Vale a pena dar uma olhada!
É isso aí pessoALL, fico por aqui!