• Nenhum resultado encontrado

3   Âmbar: Serviço Multiplataforma De Foto­Memórias

3.2   Desafios computacionais

3.3.1   Modelo de domínio

Os conceitos presentes na plataforma e refletidos, consequentemente, no serviço e nas aplicações­cliente vêm de um entendimento dos elementos e

problemas no domínio e de uma solução projetada para eles, formando um modelo   do   domínio   (Figura   4).   Esse   modelo   termina   por   determinar   as funcionalidades e recursos da plataforma e aplicações desenvolvidas a partir dela. Figura 4: Modelo de domínio da plataforma Fonte: o autor Neste modelo, a foto é um conceito central. Cada foto é representada na plataforma como a associação de uma imagem às descrições dadas pelos

usuários,   e   os   metadados   extraídos   da   imagem   automaticamente   pela plataforma. As descrições de uma foto permitem que os usuários registrem

as   memórias   associadas   a   ela.   Essas   descrições   são   representadas   na plataforma através de um conjunto de “atributos” textuais, com significados determinados,   e   uma   coleção   de  marcadores  (descrições   associadas   a

pontos na imagem).

Os atributos escolhidos remetem a anotações encontradas no verso de fotografias impressas. O “título” identifica e resume o que a foto representa, o “período” e o “lugar” descrevem respectivamente, quando e onde teriam ocorrido   o   evento   registrado   na   foto.   E   o   atributo   “descrição”   permite adicionar detalhes sobre o conteúdo ou o contexto da foto. Há ainda um atributo para descrever os autores da foto.

As   descrições   de   uma   foto   incluem   também   uma   coleção   de marcadores.   Cada   marcador   consiste   em   uma   descrição   textual   que   é associada a um ponto específico da imagem. Eles simplificam a descrição de pessoas, animais ou objetos retratados na foto.

A separação da descrição de uma foto nesses diversos atributos, ao invés   de   utilizar   uma   descrição   textual   única,   dá   maior   flexibilidade   a representação   dessas   descrições   na   interface   das   aplicações­cliente.   Por exemplo, alguns desses atributos podem ser omitidos ou representados com características diferentes (como fonte, tamanho e posicionamento).

Os metadados da foto são informações extraídas da própria imagem. O núcleo   extrai   um   subconjunto   de   metadados   que   tem   utilidade   para   o desenvolvimento   das   aplicações   ou   para   o   uso   das   fotos   em   si.   Outros, entretanto,  podem estar  presentes  nas  imagens.  Os  metadados  utilizados podem conter informações sobre a imagem em si ou sobre o contexto em que ela foi capturada, como dados sobre a câmera, geolocalização e o timestamp (data e hora) da captura.

Alguns desses dados podem, parcialmente, se sobrepor a informações presentes   nas   descrições,   como   as   informações   de   tempo   e   lugar. Entretanto, enquanto os metadados podem ser mais precisos, as descrições permitem uma visão de mais alto nível e também mais pessoal. Por exemplo, invés de utilizar coordenadas de GPS ou um endereço para descrever um local, um usuário pode utilizar o nome com que ele se refere àquele lugar, como “casa dos meus pais” ou “campus da universidade”.

A   plataforma   utiliza   o   conceito   de  álbuns  como   uma   forma   de

organizar conjuntos de fotos relacionadas. Álbuns são identificados, para os usuários, através de um nome e uma imagem de capa.

Além   de   agrupar   fotos,   álbuns   podem   conter   outros   álbuns.   Isto permite organizar as fotos utilizando diferentes níveis de detalhamento. Por sua vez, um mesmo álbum ou foto pode estar contido em diferentes outros álbuns.   Dessa   forma,   usuários   podem   aplicar   diferentes   critérios   de classificação, sem ter de lidar com fotos ou álbuns duplicados.

A plataforma provê ainda um outro nível para o agrupamento de fotos, os  contextos.   Os   contextos   permitem   separar   grupos   de   fotos   e   álbuns

compartilhados   entre   diferentes   conjuntos   de   usuários.   Isto   é,   para   cada contexto pode haver um conjunto diferente de usuários que pode acessá­lo.

Para   tanto,   contextos   permitem   associar   níveis   de  permissão  a

usuários. O menor nível, o de leitura, permite a visualização do contexto e de seu conteúdo. O segundo nível (escrita) adiciona a permissão para alterar o conteúdo do contexto. Isto é, modificar, remover ou adicionar fotos e álbuns. O   último   nível   inclui   permissões   para   gerenciar   o   contexto.   Isto   inclui adicionar e remover usuários e modificar as permissões deles.

Além das permissões por usuário, um contexto possui um nível de visibilidade, que define o nível de permissão padrão, naquele contexto, para outros   usuários   da   mesma   instância   do   serviço.   A   visibilidade   pode   ser “privada”,   que   restringe   o   acesso   apenas   aos   usuários   com   permissões definidas, “Visível para outros” ou “Editável por outros”, que permite que outros usuários visualizem ou editem o contexto, respectivamente.

Para   ajudar   no   uso   colaborativo   das   foto­memórias,   a   plataforma registra   também   um   conjunto   de   “eventos”,   que   representam   alterações

feitas   pelos   usuários   nos   elementos   de   um   contexto,   tais   como   adição   e remoção de fotos. Esses registros são importantes para permitir a usuários identificarem as mudanças realizadas por outros usuários em um contexto compartilhado.

Cada  usuário  é   representado   na   plataforma   em   função   de   sua

identidade. Do ponto de vista do sistema, esta corresponde, essencialmente, aos   seus   dados   de   autenticação.   Mas,   para   que   os   usuários   possam reconhecer uns aos outros, os dados de usuário incluem também um nome e foto de perfil.

3.3.2  Núcleo

O   núcleo   é   o   componente   de   software   responsável   por   oferecer   o serviço da plataforma. Ele provê uma API RESTful como interface do serviço

para as aplicações­cliente. O núcleo está organizado em uma arquitetura em camadas (Figura 5) e foi implementado como uma biblioteca em C++. Além de facilitar eventuais expansões, essa estrutura também potencializa o reuso do serviço, já que uma instância do serviço é facilmente criada através do uso dessa biblioteca. Figura 5: Arquitetura do núcleo Fonte: o autor A camada de domínio reúne as entidades que representam o modelo de   domínio   da   plataforma.   Essas   entidades   são   utilizadas   por   todas   as outras   camadas   do   núcleo.   A  camada   de   persistência   é   responsável   pelo armazenamento   de   dados.   Para   tanto,   esta   camada   é   dividida   em   dois módulos. O primeiro realiza a interação com o banco de dados (BD) e expõe uma interface de mais alto nível. Já o segundo é composto por um conjunto de DAOs (Data Access Objects), que interagem com o primeiro módulo para realizar a persistência das entidades do domínio. A camada de serviços é responsável por realizar as operações que são oferecidas pelo serviço da plataforma e garantir que as regras de negócio sejam cumpridas. Essa camada provê ainda componentes responsáveis pela manipulação de imagens e configurações do serviço. A camada da API provê uma interface RESTful para os clientes do serviço. Esta camada é composta por controladores, que proveem as diferentes operações expostas pela  API.

Estes controladores utilizam de um conjunto de componentes de serialização que realizam a conversão entre as classes que representam as entidades do domínio   e   um   formato   que   pode   ser   exposto   ou   recebido   pela  API.   Essa camada abrange também componentes responsáveis por tratar elementos do protocolo HTTP e aspectos da comunicação em rede.

Além   dos   componentes   presentes   no   núcleo,   aplicações   precisam prover outros elementos para permitir o uso do serviço: uma interface que permita aos usuários controlar o serviço (pelo menos iniciá­lo e finalizá­lo); e componentes   para   realizar   a   configuração   do   serviço   de   acordo   com   as características da plataforma de uso e da aplicação.

Para implementar as camadas do núcleo foram escolhidas tecnologias e bibliotecas que fossem portáveis e tivessem um uso reduzido de recursos computacionais,   de   modo   a   permitir   o   uso   da   solução   em   um   conjunto amplo   de   dispositivos   e   plataformas.   Assim,   por   exemplo,   o   SQLite21  foi utilizado pois é uma solução leve para persistência de dados. Além disso, ele provê   uma  engine  SQL   que   executa   no   mesmo   processo   da   aplicação, através de uma biblioteca. Isso evita a dependência a outros processos ou serviços, o que contribui para tornar o   núcleo do Âmbar auto­contido. A biblioteca   Civetweb22  também   contribui   para   isso.   Ela   provê   um   servidor HTTP(S)   que   pode   ser   embarcado   junto   com   a  aplicação,   em   um  mesmo processo.

Assim, o núcleo pode ser adaptado a diferentes dispositivos, sistemas operacionais e linguagens de programação. As tecnologias, bibliotecas e a linguagem   adotadas   permitem   que   o   serviço   seja   utilizado   mesmo   em dispositivos   com   recursos   de  hardware  limitados,   tais   como,   dispositivos móveis   (e.g.  tablets  e  smartphones)   ou  single   board   computers  (e.g. Raspyberry   Pi23  e,   possivelmente,   até   algumas   versões   de   Arduino   que executam Linux, como o Arduino Yún24) e através dos principais sistemas operacionais   da   atualidade.   É   possível,   ainda,   portar   o   núcleo   para diferentes   linguagens   de   programação,   utilizando  bindings.   Através   do

21sqlite.org.

22github.com/civetweb/civetweb. 23raspberrypi.org.

projeto swig25, por exemplo, é possível construir bindings de C++ para mais de 20 linguagens.

O   desenvolvimento   do   núcleo   como   uma   biblioteca   permite   ainda embarcar o serviço junto a uma aplicação­cliente em um mesmo software. Além de poder simplificar a implantação dessas aplicações, isto possibilita a criação de soluções “auto contidas”, isto é, que não dependam de serviços externos   e,   portanto,  possam   ser   utilizadas   mesmo   sem   conectividade.   O acesso   direto   ao   núcleo   pode   também   simplificar   a   administração   pelos usuários,  pois  a  aplicação  possui  um  acesso  privilegiado  aos  dados  e   ao próprio serviço.

O   uso   embarcado   do   núcleo   facilita   também   o   desenvolvimento   de appliances,   visto   que   é   possível   oferecer,   em   um   único   dispositivo   com propósito  específico,  tanto uma  interface  direta para os  usuários, quanto uma   instância   do   serviço.   Consequentemente,   isso   permitiria   a   outras aplicações   controlarem   remotamente   a   instância   do   serviço   que   está embarcada.

3.3.2.1  API do serviço

A API é a interface que expõe o serviço implementado no núcleo para as aplicações­cliente. A API utiliza o protocolo HTTP para comunicação com as aplicações­cliente e adota uma arquitetura RESTful. O uso desse estilo de arquitetura   simplifica   a   representação   do   modelo   do   domínio   na  API,   e permite   também   criar   um   modo   uniforme   de   interagir   com   as   entidades representadas.

Em   arquiteturas   RESTful,   as   capacidades   do   serviço   são representadas através de  resources, que podem ser acessados por meio de URLs (Universal Resource Locators). Ações podem ser executadas sobre esses resources   através   de   um   conjunto   restrito   de   verbos.   Em   serviços   que utilizam o protocolo HTTP, esses verbos são mapeados para os métodos do protocolo26, tais como, GET, POST e DELETE.

25swig.org. 26restfulapi.net

As   rotas   de   uma  API   RESTful   correspondem   à   parte   da   URL   que permite localizar um resource dentro de um servidor. Elas apresentam uma estrutura hierárquica que permite representar relações entre conceitos.

A rota base da API do serviço, a “raiz”, apresenta informações sobre a instância do serviço, como a versão da  API e do núcleo e um identificador para   usuários   dessa   instância.   Como   a  API   utiliza   versionamento semântico27, isso permite que usuários e sistemas de terceiros identifiquem o serviço   que   está   sendo   executado   e   se   o   mesmo   é   compatível   com   a aplicação­cliente.

Versionamento semântico, é uma forma de indicar através do número de versão de um software informações sobre sua compatibilidade com outras versões. Para isso é utilizado um formato com três números separados por pontos   (‘X.Y.Z’),   onde   o   primeiro   número   identifica   mudanças   sem   retro­ compatibilidade,   o   segundo   número   marca   adição   de  features  com   retro­ compatibilidade e o terceiro número representa correções.

A raiz da API também lista o conjunto de rotas acessíveis através dela, o   que   funciona   como   auto­documentação.   Cada   uma   dessas   rotas representa uma coleção de entidades de um mesmo tipo. Elas seguem um “arquétipo” de resouce (collection). Esses arquétipos atuam como padrões de design, mas aplicados a modelagem de APIs RESTful. Outros três arquétipos são identificados por Massé (2012): document, store e controller.

Cada membro de uma coleção pode ser acessado através de uma “sub­ rota”   dela,   utilizando   um   identificador   único   da   entidade   ou,   em   alguns casos, um nome que referencia a entidade de forma relativa a outros dados da requisição. A rota “/users/me”, por exemplo, permite acessar dados do próprio usuário que fez a requisição. Essas entidades seguem o arquétipo “document”.

Propriedades   de   uma   entidade   podem,   por   sua   vez,   ser   acessadas através   de   “sub­rotas”   da   entidade,   utilizando   o   nome   da   propriedade. Algumas dessas propriedades podem ser também coleções, sob as quais o mesmo padrão de acesso, definido anteriormente, se aplica. 

Dessa forma, é possível identificar um padrão geral para as rotas da API   (Figura   6).   Isto   é,   na   base   da  API   são   referenciadas   as   diferentes coleções. Abaixo delas é possível acessar as entidades ou itens das coleções que podem ter um conjunto de propriedades.  A interação com essas coleções e entidades da API também é feita de modo uniforme, de acordo com o tipo da rota (Tabela 3). Cada coleção provê operações para listar e adicionar itens, através dos métodos GET e POST, respectivamente. Já a atualização ou remoção de cada item é feita através da rota   que   o   identifica,   utilizando   os   métodos   POST   e   DELETE, respectivamente. Esta rota também pode ser utilizada para acessar dados de uma entidade, usando o método GET. Figura 6: Estrutura geral de rotas da API e alguns exemplos /<coleção>/<entidade>/<propriedade> /photos/1/image /contexts/123/photos Fonte: o autor A interação com as propriedades de uma entidade, dependendo de sua semântica, pode ocorrer de forma similar a uma entidade ou coleção. Em ambos os casos, uma propriedade não pode ser removida, apenas alterada ou lida.

Os   dados   trocados   entre   as   aplicações­cliente   e   a  API   são representados no formato JSON28 (JavaScript Object Notation), exceto quando são utilizados tipos específicos, como imagens.

Tabela 3: Comportamento geral para interagir com rotas da API Tipo da

rota Método Operação Exemplos de rota

Coleção GET Lista todos os membros /events/contexts

POST Adiciona membro

Entidade GET Obtém entidade /albums/1

/photos/123 POST Altera entidade

DELETE Remove entidade Propriedade

(entidade)

GET Obtém propriedade /users/me/profile-image

POST Altera propriedade

Propriedade (coleção)

GET Lista todos os membros

/photos/1/markers

POST Adiciona membro

Fonte: o autor

Algumas rotas da API permitem o uso de parâmetros na url (Figura 7) para   filtrar   a   coleção   de   itens   retornados   ou   solicitar   mudanças   na representação deles.

Nem   todas   operações   feitas   através   da  API   podem   ser   mapeadas diretamente ao padrão descrito. É o caso das operações em lote feitas sobre os álbuns, uma vez que elas atuam sobre os itens de um ou mais álbuns para permitir copiar, mover ou excluir esses itens de forma atômica. Além dos verbos e rotas utilizados, os dados retornados pela  API e operações realizadas através dela também são modificados de acordo com o usuário que está autenticado, seguindo as permissões definidas no modelo de dados do serviço. Figura 7: Exemplo do uso de parâmetros em requisições à API. a) /albums/123/photos?query="família" b) /albums/456?expand-items=true

a) representa uma busca por fotos no álbum '123' que contenham o termo "família". Já em b), a representação do álbum '456' é modificada para incluir também dados das fotos e álbuns contidos nele, de forma a reduzir número de consultas.

Fonte: o autor Para se autenticar, clientes requisitam a criação de uma nova sessão, passando suas credenciais de acesso, i.e., login e senha. Após validar as credenciais, o núcleo cria a sessão para aquele cliente e gera um token. Isto é, um identificador da sessão que será utilizado pelo cliente para realizar as requisições futuras.

O  token  é   enviado   ao   cliente   através   de   um  cookie  e   no   corpo   da requisição.   Isso   permite   um   gerenciamento   automático   da   sessão   por clientes que tem suporte a  cookies. Mas também dá suporte aqueles que tenham restrições de acesso aos cookies. Esse é o caso de clientes Javascript que executam no navegador. Devido a questões de segurança, o navegador impede que eles acessem cookies que não venham do seu servidor de origem. Também   para   permitir   esse   tipo   de   cliente,   a  API   dá   suporte   a requisições   CORS29  (Cross­Origin  Resource  Sharing),   um   protocolo   que permite   a   páginas   web   acessarem   alguns   tipos   de   recursos   “restritos” provenientes de domínios diferentes da origem.

Sem essas medidas, clientes Web não poderiam acessar uma instância do   serviço   em   um   domínio   ou   servidor   diferente.   Isso   dificultaria   o desenvolvimento desses clientes de forma independente, bem como o seu acesso a diferentes instâncias do serviço.

Para   acessar  resources  não   públicos   da  API,   os   clientes   precisam enviar a cada requisição o token recebido. A partir dele, o núcleo irá verificar as   permissões   do   usuário.   Caso   o   usuário   não   possua   as   permissões suficientes   ou   não   esteja   autenticado   (token  expirou,   está   inválido   ou ausente), a API irá retornar uma mensagem de erro. Os status codes (códigos de estado) do protocolo HTTP são utilizados para comunicar a causa do erro. No caso de coleções a que o cliente tenha acesso a apenas uma parte dos itens, o núcleo oculta automaticamente os dados que o cliente não tenha permissão para visualizar. Ao acessar a coleção de contextos, por exemplo, são listados apenas aqueles que o usuário tem pelo menos permissão de leitura. 29 developer.mozilla.org/en-US/docs/Web/HTTP/CORS

3.3.2.2  Aplicações do núcleo

Para   prover   os   serviços   da   plataforma,   o   núcleo,   por   ser   uma biblioteca,   precisa   estar   presente   em   uma   aplicação.   Essa   aplicação   é responsável   por   controlar   a   execução   do   serviço,   i.e.   inicialização   e finalização. Além disso, ela irá prover uma interface para os usuários de modo a permitir a configuração do serviço.

3.3.2.2.1  Âmbar Daemon

O   Âmbar   Daemon   é   uma   aplicação   de   linha   de   comando   para   a execução   do   serviço   Âmbar.   Ela   foi   desenvolvida   para   executar primariamente em background, i.e. como um daemon, de modo a poder ser implantada   em   servidores   ou   dispositivos   embarcados.   Mas   ela   também pode   ser   utilizada  em   computadores   pessoais,   e.g.  desktops  e  notebooks, sem   interferir   nas   atividades   do   usuário.   A   aplicação   é   compatível   com sistemas Unix, e.g. Linux e MacOS, e com Windows, mas atualmente foi testada apenas no Linux.  O Âmbar Daemon utiliza o framework Poco30, que permite a execução dele como um daemon Unix ou como um Windows service, de acordo com o sistema operacional. Ele pode também ser executada em modo interativo, i.e. conectado à linha de comando.

A   interface   do   Âmbar   Daemon   provê   opções   para   definir   as configurações do serviço, e.g. porta utilizada, diretórios para armazenamento de   dados,   configurações   de   log,   etc.   Essas   mesmas   configurações   podem também ser fornecidas através de um arquivo de configuração (utilizando formato   JSON).   Isso   permite   persistir   as   configurações   entre   execuções. Caso   as   configurações   não   sejam   informadas,   o   núcleo   utiliza   valores padrão.

3.3.2.2.2  Âmbar Android Server

O   Âmbar   Android   Server   é   um   componente   criado   para   permitir 30pocoproject.org

executar o serviço Âmbar em dispositivos Android. Ele é oferecido através de uma   biblioteca   Android   para   permitir   seu   uso   integrado   a   um   aplicativo cliente (ver seção 3.3.3.2) ou em uma aplicação isolada. 

O   componente   principal   da   biblioteca   é   um   serviço   Android31 (AmbarAndroidService), que é responsável por configurar, iniciar e finalizar o serviço Âmbar. Para isso, ele interage com o núcleo, com ajuda de outros componentes, utilizando bindings JNI (Java Native Interface). 

O  AmbarAndroidService  provê   ainda   a   integração   necessária   com   a plataforma Android. Ele permite receber comandos de uma aplicação para iniciar   ou   finalizar   o   serviço   Âmbar,   e   pode,   por   sua   vez,   notificá­la   em relação   a   mudanças   no   estado   desse   serviço.   O  AmbarAndroidService também apresenta um ciclo de vida, gerenciado pelo Android, que permite a ele   reagir,   por   exemplo,     à   finalização   da   aplicação   ou   serviço,   por   um usuário ou pelo sistema.

Para reduzir a chance do serviço Âmbar ser finalizada pelo Android enquanto   estiver   em   uso,   e.g.   devido   a   baixa   memória,   e   permitir   aos usuários identificarem quando ele está ativo, o AmbarAndroidService utiliza o   recurso   de   “foreground   service”32  do   Android.   Esse   recurso   indica   ao Android   que   o   usuário   está   interagindo   ativamente   com   o   serviço.   Ele também cria uma notificação que permite aos usuários visualizarem que o serviço está em execução. Essa notificação também inclui um controle que permite   ao   usuário   finalizar   o   serviço,   caso   deseje.   Quando   o   serviço   é finalizado (pelo usuário ou pelo sistema), ou caso saia de “foreground”, a notificação é removida.

Documentos relacionados