Introdução

A persistência de dados é o armazenamento das informações em dispositivos físicos não-voláteis para posterior recuperação e reuso. No caso da persistência em objetos, o grande benefício é que estes objetos serão guardados de forma a não serem destruídos, mesmo após o término do programa no qual foram criados ou na reinicialização do sistema, e assim podendo ser recuperados e reusados em outros programas que lidam com o mesmo tipo de objeto.


Problema:

Implementar no EPOS o uso de objetos persistentes, para que mesmo após a reinicialização do sistema os objetos criados anteriormente possam ser recuperados. Para isso é necessário a utilização de um índice, construído de maneira a ser único e recriável, para identificar cada objeto. Além do índice, é necessário garantir que os dados armazenados na memória não volátil sejam atualizados quando os dados no sistema são alterados, mantendo confiabilidade e corretude.


Solução Proposta:

Nossa solução consiste na criação um mecanismo de persistência de utilizando o conceito de programação orientada à aspectos (AOP). Este conceito permite uma maior clareza no código resultando, visto que pode-se inserir ou remover características diversas de um objeto sem alterar a sua classe correspondente. Na programação orientada à aspectos são utilizados pontos de junção (joinpoints) que são onde os aspectos vão interceptar a execução e tomar o controle sobre o próximo código que será executado. Na nossa proposta utilizamos joinpoints na alteração dos objetos. Sendo assim, para o usuário, a persistência acontecerá de forma automática, ou seja, não será necessário chamar diretamente a função de persistência do objeto, isso acontecerá no momento em que o objeto for alterado. Cada objeto recebe um índice único que será gerado pelo nome e pelo valor de todos os atributos dos próprios objetos.


Descrição

Para garantir que os dados persistidos em memória não volátil sejam atualizados usaremos AOP (Aspect Oriented Programming) sendo suportado pela extensão da linguagem C++ chamada Aspectc++. Com AOP é possível incluir pontos de junção no código e realizar uma operação de criação, atualização ou remoção de objetos da camada de persistência sempre que necessário.

Após exaustivos testes, verificamos que dentro desse contexto o AspectC++ não funciona corretamente. Sendo assim foi implementada a ideia de como seria a estrutura de utilização desse recurso para a persistência de objetos.

Na criação de um objeto persistente um índice único será criado. Utilizando esse índice como parâmetro será executada uma busca na memória, na tentativa de encontrar uma referência na memória do objeto que está sendo criado. Caso retorne um valor negativo este objeto será salvo na memória e o objeto criado. Caso a busca retorne o endereço do objeto, ele será recuperado e utilizado para ser referenciado pelo objeto que está sendo criado.

Todas as alterações em objetos persistidos serão feita através do mesmo mecanismo que salva e recupera as informações, para garantir a consistência e a persistência dos novos dados do objeto.

O índice único para cada objeto será gerado através dos atributos que ele possue e de um identificador de sua classe. Dentre todos os atributos, quais efetivamente serão parte do identificador será determinado pelo programador através da função virtual "toString()". Este método é virtual e será implementado pela classe filha de PersistenceObject.

Como demostração foi criada uma variação da classe PersistenceObject chamada PersistenceObjectTxt que utiliza o recurso de persistência em arquivos de texto dos objetos para que seja possível a visualização dos resultados dos testes.

 

Testes

Simulação salvamento de Objetos em Memória


Para verificar a corretude do projeto nos testes, decidimos simular a persistência dos objetos e sua respectiva recuperação através da persistência através de arquivos de texto. Isto está sendo simulado na classe MemoryComm, já que a mesma é a responsável por se comunicar com a memória, salvar e restaurar objetos. Esta classe possuirá os métodos write e read que escreverá e lerá um objeto em memória, respectivamente, que dentro deste contexto será a escrita e leitura do objeto a partir de um arquivo de texto.


Passos Gerais dos Testes Unitários

-Teste de Comunicação com a Memória
1)Criação de objeto persistente
2)Escrita do objeto na memória
3)Recuperação do objeto da memória
4)Verificação da igualdade entre o objeto escrito e o recuperado

-Teste sobre Persistência do Objeto
1)Criação de objeto persistente
2)Alteração de um atributo do objeto
3)Verificação da atualização do atributo no objeto persistente

-Teste de Geração de Índice Único
1)Criação de objeto1
2)Geração do índice do objeto1 e salvamento na hashtable
3)Recuperação do objeto1 a partir da hashtable
4)Geração de novo índice para objeto1
5)Verificação da igualdade entre o primeiro e o segundo índices gerados
6)Geração de objeto2 (idêntico ao objeto1) e do seu índice
7)verificacão da igualdade entre os índices do objeto1 e objeto2
8)Geração de objeto3 (diferente do objeto1) e do seu índice
9)Verificação da diferença entre os índices do objeto1 e objeto3


Código


PersistenceObject.h

#ifndef PERSISTENCEOBJECT_H_
#define PERSISTENCEOBJECT_H_

class PersistenceObject {
public:
    PersistenceObject();
    virtual ~PersistenceObject();
    int getId();
    void setId(int id);
    void setInside(PersistenceObject *inside);
    PersistenceObject & operator =(const PersistenceObject other);

   void setPath(char* pathObj){path = pathObj;}

   char* getPath(){return path;}

    void setId(idObj){id = idObj;}

    virtual string* toString();

    virtual void setValuesFromString(string str);
private:
    int id;
};

#endif /* PERSISTENCEOBJECT_H_ */


PersistenceObject.cc

#include <persistence/PersistenceObject.h>

PersistenceObject::PersistenceObject() {

    id = 0;
    path = 0;
    name = 0;
    size = 0;
}

PersistenceObject::~PersistenceObject() {
}

void PersistenceObject::setId(int id) {
    this->id = id;
}

PersistenceObject & PersistenceObject::operator =(const PersistenceObject other){
    *this = other;
    return *this;
}


PersistenceAttribute.h

template<class Type>

class PersistentAttribute {
private:
    Type value;
public:
    PersistentAttribute(Type value) : value(value) {};
    PersistentAttribute() {};
    operator Type();

    PersistentAttribute<Type> & operator =(const Type &other);
    PersistentAttribute<Type> & operator +=(const Type &other);
    PersistentAttribute<Type> & operator -=(const Type &other);
    PersistentAttribute<Type> & operator *=(const Type &other);
    PersistentAttribute<Type> & operator /=(const Type &other);
    PersistentAttribute<Type> & operator %=(const Type &other);
    PersistentAttribute<Type> & operator &=(const Type &other);
    PersistentAttribute<Type> & operator |=(const Type &other);
    PersistentAttribute<Type> & operator ^=(const Type &other);
    PersistentAttribute<Type> & operator <<=(const Type &other);
    PersistentAttribute<Type> & operator >>=(const Type &other);
    PersistentAttribute<Type> & operator ++();
    Type operator ++(int);
    PersistentAttribute<Type> & operator --();
    Type operator --(int);
    Type operator ->();
};
#endif

PersistenceAttribute.cc

#include <persistence/PersistentAttribute.h>

template<class Type>
PersistentAttribute<Type>::operator Type()
{
    return value;
}

template<class Type>
PersistentAttribute<Type> & PersistentAttribute<Type>::operator =(const Type &other)
{
    value = other;
    return *this;
}

template<class Type>
PersistentAttribute<Type> & PersistentAttribute<Type>::operator +=(const Type &other)
{
    value += other;
    return *this;
}

template<class Type>
PersistentAttribute<Type> & PersistentAttribute<Type>::operator -=(const Type &other)
{
    value -= other;
    return *this;
}

template<class Type>
PersistentAttribute<Type> & PersistentAttribute<Type>::operator *=(const Type &other)
{
    value *= other;
    return *this;
}

template<class Type>
PersistentAttribute<Type> & PersistentAttribute<Type>::operator /=(const Type &other)
{
    value /= other;
    return *this;
}

template<class Type>
PersistentAttribute<Type> & PersistentAttribute<Type>::operator %=(const Type &other)
{
    value %= other;
    return *this;
}

template<class Type>
PersistentAttribute<Type> & PersistentAttribute<Type>::operator &=(const Type &other)
{
    value &= other;
    return *this;
}

template<class Type>
PersistentAttribute<Type> & PersistentAttribute<Type>::operator |=(const Type &other)
{
    value |= other;
    return *this;
}

template<class Type>
PersistentAttribute<Type> & PersistentAttribute<Type>::operator ^=(const Type &other)
{
    value ^= other;
    return *this;
}

template<class Type>
PersistentAttribute<Type> & PersistentAttribute<Type>::operator <<=(const Type &other)
{
    value <<= other;
    return *this;
}

template<class Type>
PersistentAttribute<Type> & PersistentAttribute<Type>::operator >>=(const Type &other)
{
    value >>= other;
    return *this;
}

template<class Type>
PersistentAttribute<Type> & PersistentAttribute<Type>::operator ++()
{
    value ++;
    return *this;
}

template<class Type>
Type PersistentAttribute<Type>::operator ++(int)
{
    Type old = value;
    value ++;
    return old;
}

template<class Type>
PersistentAttribute<Type> & PersistentAttribute<Type>::operator --()
{
    value --;
    return *this;
}

template<class Type>
Type PersistentAttribute<Type>::operator --(int)
{
    Type old = value;
    value --;
    return old;
}

template<class Type>
Type PersistentAttribute<Type>::operator ->()
{
    return value;
}

PersistenceTrigger.h

#ifndef PERSISTENCETRIGGER_AH
#define PERSISTENCETRIGGER_AH

#include <persistence/persistenceManager.h>
#include <persistence/persistenceIdFactory.h>

PersistenceManager persistenceManager;
PersistenceIdFactory persistenceIdFactory;

aspect PersistenceTrigger {
    
    pointcut persistent() = derived("PersistenceObject");

    pointcut setters() = "% ...::...::set%(...)";
    
    pointcut operators() = "% ...::operator %(...)";
    
    pointcut constr() = construction(persistent());
    
    pointcut destr() = destruction(persistent());
    
    advice constr() : after() {
        int id = persistenceIdFactory.generateId();
        PersistenceObject *obj = (PersistenceObject) tjp->target();
        if (persistenceManager.hasObject(id)) {
            persistenceManager.updateObject(obj, id);
        } else {
            persistenceManager.addObjectObject(obj);
        }
    }

    advice destr() : before() {
        PersistenceObject *obj = (PersistenceObject) tjp->target();
        if (persistenceManager.hasObject(id)) {
            persistenceManager.removeObject(obj->getId());
        }
    }
    
    advice execution(setters()) : around() {
        tjp->proceed();
        PersistenceObject *obj = (PersistenceObject) tjp->target();
        if (persistenceManager.hasObject(id)) {
            persistenceManager.updateObject(obj, id);
        } else {
            persistenceManager.addObjectObject(obj);
        }
    }
    
    advice execution(operators()) : around() {
        tjp->proceed();
        PersistenceObject *obj = (PersistenceObject) tjp->target();
        if (persistenceManager.hasObject(id)) {
            persistenceManager.updateObject(obj, id);
        } else {
            persistenceManager.addObjectObject(obj);
        }
    }
};


#endif

#include <persistence/memoryCommTxt.h>
#include <persistence/persistenceObjectTxt.h>

class PersistenceManagerTxt
{

private:

    Hash<int, PersistenceObjectTxt*> indexes;
    Simple_List<int> ids;
    PersistenceIdFactory idFactory;    


PersistenceManager.h


public:

    PersistenceManager();
        ~PersistenceManager();

        bool hasObject(int id);

    void addObject(PersistenceObject* obj);

    void removeObject(int id);

    PersistenceObjectTxt* getObject(int id);

        void updateObject(PersistenceObjectTxt* obj, int id);

};

PersistenceIdFactory.h

#include <persistence/persistenceObject.h>

class PersistenceIdFactory
{

private:

    static int count;    

public:

    PersistenceIdFactory();
        ~PersistenceIdFactory();
        int generateId(PersistenceObject* obj);

};


PersistenceIdFactory.cc

#include <persistence/persistenceIdFactory.h>

PersistenceIdFactory::PersistenceIdFactory(){
    count = 1;
}

PersistenceIdFactory::~PersistenceIdFactory(){}

int PersistenceIdFactory::generateId(PersistenceObject* obj){
    string str = obj->toString();                                                       
        int i;    
    int id = 0;                                             

    for(i = 0; i < str.length(); i++)
            id = id + int(str[i]);

        return id;
}


PersistenceManager.cc

#include <persistence/persistenceManagerTxt.h>

PersistenceManagerTxt::PersistenceManagerTxt(){
    MemoryCommTxt::readHashTable(indexes, ids);
    idFactory = new PersistenceIdFactory();
}

PersistenceManagerTxt::~PersistenceManagerTxt(){
    delete(indexes);
    delete(ids);
    delete(idFactory);
}

bool PersistenceManagerTxt::hasObject(PersistenceObjectTxt* obj){
    if(obj->getId() == 0){
        int id = idFactory->generateId(obj);
        obj->setId(id);
    }
    PersistenceObjectTxt* ob = indexes->search_key(obj->getId())->object();
    return ob != 0;
}

void PersistenceManagerTxt::addObject(PersistenceObjectTxt* obj){
    if(obj->getId() == 0){
        int id = idFactory->generateId(obj);
        obj->setId(id);
        obj->setPath("/home/taise/Desktop/OpenEPOS1.1/" + id + ".txt");
        Hash<int, PersistenceObjectTxt*>::Element * e;
        e = new Hash<int, PersistenceObjectTxt*>>::Element(obj.getId(), obj);
        indexes.insert(e);
        ids.insert(obj.getId());
    }
    MemoryCommTxt::write(indexes, ids, obj, 0);    
}

void PersistenceManagerTxt::removeObject(int id){
    MemoryComm::removeObject(indexes, ids, indexes->search_key(id)->object());
}

PersistenceObjectTxt* PersistenceManagerTxt::getObject(int id){
    return indexes->search_key(id)->object();
}

void PersistenceManager::updateObject(PersistenceObjectTxt* obj, int id){
    MemoryComm::write(indexes, ids, obj, obj->getPath());
}



Teste

#include <persistence/persistenceObjectTxt.h>
#include <vector>
#include <string>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>

using namespace std;

class PersistenceObjectTxtTest: public PersistenceObjectTxt{

private:
    char* name;
    char* age;
    char* email;

public:
    PersistenceObjectTxtTest(char* nameO, char* ageO, char* emailO){
        name = nameO;
        age = ageO;
        email = emailO;    
    }
    virtual string* toString(){
        return "name " + name + " age " + age + " email " + email;        
    }

    virtual void setValuesFromString(string str){
        string buf;
            stringstream ss(str);
            vector<string> tokens;
        while (ss >> buf)
                tokens.push_back(buf);
        name = tokens[1];
        age = tokens[3];
        email = tokens[5];
    }

}

int main(){
    PersistenceObjectTxtTest test = new PersistenceObjectTxtTest(´alunoA´, ´20´, ´alunoA@usc.br´);
    File *arquivo = fopen(test -> getPath());
    if(arquivo != NULL)
            printf("Arquivo foi aberto com sucesso.");
     else
            printf("Nao foi possivel abrir o arquivo.");

    return 0;
}





Projeto Lógico

Diagrama de Classes

 

 

 


Referências

- Aspectc++ - http://www.aspectc.org
/>

- Typeid Operator - http://msdn.microsoft.com/en-us/library/fyf39xec.aspx

- Type_Info Class - http://www.cplusplus.com/reference/std/typeinfo/type_info/

- Run-time type information (RTTI) - http://msdn.microsoft.com/en-us/library/b2ay8610.aspx

- Template - http://www.lisha.ufsc.br/teaching/dos/smp.pdf

                 - http://www.cplusplus.com/doc/tutorial/templates/

Tags:

Código

so2.rar