Montag, 28. Oktober 2013

BARACUS from Scratch - Part 5 - Advanced Persistence Mapping

Table of Contents

Previous Tutorial : Inversion of Control & Dependency Injection

In this tutorial I am going to show You, how to make use of lazy loading and object navigation using references. This can make Your application development a lot easier, because many problems in the data world can be solved by navigation instead of writing a query.

This tutorial is based on the tutorial part 3, so if You have not read it, start from there.


Download the source code to this tutorial from Github

Step 1 : The basics


We have modelled the BankAccount in the prior tutorial and fitted it with a DAO and a RowMapper. This aspect of using a row mapper ist pretty vital for lazy loading. Baracus does not rely on annotation based code generation like hibernate nor on reflection based object loading. It follows a very straight forward rowmapping strategy leaving a lot of control in the hands of the developer. This includes choosing to have lazy loading or eager loading in the place where the row data is mapped.

Imagine, we create a Customer entity. One customer may have multiple BankAccounts but a BankAccount may only have one designated customer - a classic 1:N or N:1 situation we meet in every well designed database environment nowadays.

The proceeding is following the four steps mentioned in the prior tutorial (entity, dao+rowmapper, migr8 mode, bean wiring)

ONE - The entity


So at first, we create a bare CustomerEntity having only some properties : Name and  FirstName (hey, this is a demo only ;-)) and it will look somewhat like this :

package org.baracus.model;

import org.baracus.orm.ModelBase;
import org.baracus.orm.Field;
import org.baracus.orm.FieldList;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */
public class Customer extends ModelBase {

    public static final String TABLE_CUSTOMER = "customer";

    private static int columnIndex= ModelBase.fieldList.size();

    private String lastName;
    private String firstName;

    public static final FieldList fieldList = new FieldList(Customer.class.getSimpleName());
    public static final Field lastNameCol = new Field("last_name", columnIndex++);
    public static final Field firstNameCol = new Field("first_name", columnIndex++);


    static {
        fieldList.add(ModelBase.fieldList);
        fieldList.add(lastNameCol);
        fieldList.add(firstNameCol);
    }

    public Customer() {
        super(TABLE_CUSTOMER);
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public static FieldList getFieldList() {
        return fieldList;
    }

    public static Field getLastNameCol() {
        return lastNameCol;
    }

    public static Field getFirstNameCol() {
        return firstNameCol;
    }
}


We are playing the same game again like we did on the BankAccount. Define fields, define tablename, define column metadata. Next we are going to need is a dao

TWO - The DAO


The DAO is responsible for the mapping. To map a customer from DB and vice versa we need a DAO Implementation carrying a RowMapper implementation :

package org.baracus.dao;

import android.content.ContentValues;
import android.database.Cursor;
import org.baracus.model.BankAccount;
import org.baracus.model.Customer;
import org.baracus.orm.Field;
import org.baracus.orm.FieldList;

import static org.baracus.model.Customer.*;
import static org.baracus.orm.ModelBase.idCol;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 * To change this template use File | Settings | File Templates.
 */
public class CustomerDao extends BaseDao<Customer> {

    /**
     * Lock the DAO of
     */
    public CustomerDao() {
        super(Customer.class);
    }

    private final RowMapper<Customer> rowMapper = new RowMapper<Customer>() {
        @Override
        public Customer from(Cursor c) {
            Customer result = new Customer();
            result.setId(c.getLong(idCol.fieldIndex));
            result.setLastName(c.getString(lastNameCol.fieldIndex));
            result.setFirstName(c.getString(firstNameCol.fieldIndex));
            result.setTransient(false);
            return result;        }

        @Override
        public String getAffectedTable() {
            return Customer.TABLE_CUSTOMER;
        }

        @Override
        public FieldList getFieldList() {
            return Customer.fieldList;
        }

        @Override
        public Field getNameField() {
            return Customer.lastNameCol;
        }

        @Override
        public ContentValues getContentValues(Customer customer) {
            ContentValues result = new ContentValues();
            if (customer.getId() != null) { result.put(idCol.fieldName, customer.getId()); }
            if (customer.getLastName() != null) { result.put(lastNameCol.fieldName, customer.getLastName()); }
            if (customer.getFirstName() != null) { result.put(firstNameCol.fieldName, customer.getFirstName()); }
            return result;
        }
    };

    @Override
    public RowMapper<Customer> getRowMapper() {
        return rowMapper;
    }
}


You can mostly copy the BankAccountDao and modify it properly using the Customer entity.

THREE - The migr8 model


In order to let android create the table, we need to make use of migr8. Because this application is a "already running" one, we create a new MigrationStep implementation; this will show You to make use of the extensible migration system allowing to migrate any application version to the latest one:

package org.baracus.migr8;

import android.database.sqlite.SQLiteDatabase;
import org.baracus.model.BankAccount;
import org.baracus.model.Customer;
import org.baracus.util.Logger;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 * To change this template use File | Settings | File Templates.
 */
public class ModelVersion101 implements MigrationStep {


    private static final Logger logger = new Logger(ModelVersion101.class);
    @Override
    public void applyVersion(SQLiteDatabase db) {

        String stmt  = "CREATE TABLE " + Customer.TABLE_CUSTOMER
                + "( "+ Customer.idCol.fieldName+" INTEGER PRIMARY KEY"
                + ", "+ Customer.lastNameCol.fieldName+ " TEXT"
                + ", "+ Customer.firstNameCol.fieldName+ " TEXT"+
                  ")";
        logger.info(stmt);
        db.execSQL(stmt);

    }

    @Override
    public int getModelVersionNumber() {
        return 101;
    }
}

As You can see, we make use of the column metadata to create the CREATE TABLE-statement.

FOUR - Wire the beans


Now we wire the beans properly, this means a) registering the OpenHelper implementation and b) registering the DAO in the app initializer.

package org.baracus.application;

import android.content.Context;
import org.baracus.dao.BaracusOpenHelper;
import org.baracus.migr8.ModelVersion100;
import org.baracus.migr8.ModelVersion101;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */
public class OpenHelper extends BaracusOpenHelper {

    private static final String DATABASE_NAME="tutorial-app.db";
    private static final int TARGET_VERSION=101;

    static {
        addMigrationStep(new ModelVersion100());
        addMigrationStep(new ModelVersion101());
    }

    /**
     * Open Helper for the android database
     *
     * @param mContext              - the android context
     */
    public OpenHelper(Context mContext) {
        super(mContext, DATABASE_NAME, TARGET_VERSION);
    }
}


As you can see, we increase the TARGET_VERSION and we add our MigrationStep. Finally, we register the DAO in the ApplicationContext :

package org.baracus.application;

import org.baracus.context.BaracusApplicationContext;
import org.baracus.dao.BankAccountDao;
import org.baracus.dao.BaracusOpenHelper;
import org.baracus.dao.CustomerDao;
import org.baracus.service.BankAccountService;
import org.baracus.service.CustomerService;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */
public class ApplicationContext extends BaracusApplicationContext {

    static {
        registerBeanClass(OpenHelper.class);

        registerBeanClass(BankAccountDao.class);
        registerBeanClass(CustomerDao.class);

        registerBeanClass(CustomerService.class);
        registerBeanClass(BankAccountService.class);
    }

}

Step2 : Many-To-One relation


So far, we made the basics mapping. Now imagine, a BankAccount entity is bound to a customer. This will bring us to directly to the classical ManyToOne relation, a standard situation in any database carrying one or more table relations. This requires us to wire the BankAcount to the Customer entity and is realized using the Reference<T> interface. Because there is no dynamic typing and no code generation done with Baracus, this is the way to enable us having object relations.

So basically, at first we create a field in the BankAccount using the Reference<Customer> type and create the designated database column :


package org.baracus.model;
// ..
public class BankAccount extends ModelBase {

     // ...

    private Reference<Customer> customerReference;

    public static final Field customerIdCol = new Field("customerId", columnIndex++);


    static {
        // ...
        fieldList.add(customerIdCol);
    }
    
    // ...

    public Reference<Customer> getCustomerReference() {
        return customerReference;
    }

    public void setCustomerReference(Reference<Customer> customerReference) {
        this.customerReference = customerReference;
    }

    public Customer getCustomer() {
        return customerReference.getObject();
    }

    public void setCustomer(Customer customer) {
        this.customerReference = new ObjectReference(customer);
    }

}

As You can see, I added a reference field, a column for the Customer-Id foreign key and three getter/setter functions (two for the reference and two commodity getter/setter. As You can see, I am using the ObjectReference class for setting the customer, if the complete object is present (well it isn't in case of lazy loading, but when creating objects we normally have the objects present).

Next, we have to extend the database migration, the existing table needs to be altered. Therefore we need to create another  MigrationStep carrying the version 102 :

package org.baracus.migr8;

// ...

public class ModelVersion102 implements MigrationStep {


    private static final Logger logger = new Logger(ModelVersion102.class);
    @Override
    public void applyVersion(SQLiteDatabase db) {

        String stmt  = "ALTER TABLE " + BankAccount.TABLE_BANK_ACCOUNT
                + " ADD COLUMN "+BankAccount.customerIdCol.fieldName + " INTEGER";
        logger.info(stmt);
        db.execSQL(stmt);

    }

    @Override
    public int getModelVersionNumber() {
        return 102;
    }
}


This migration step needs to be registered to the OpenHelper implementation, whose TARGET_VERSION is increased to 102 :
package org.baracus.application;

import android.content.Context;
import org.baracus.dao.BaracusOpenHelper;
import org.baracus.migr8.ModelVersion100;
import org.baracus.migr8.ModelVersion101;
import org.baracus.migr8.ModelVersion102;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */
public class OpenHelper extends BaracusOpenHelper {

    private static final String DATABASE_NAME="tutorial-app.db";
    private static final int TARGET_VERSION=102;

    static {
        addMigrationStep(new ModelVersion100());
        addMigrationStep(new ModelVersion101());
        addMigrationStep(new ModelVersion102());
    }

    /**
     * Open Helper for the android database
     *
     * @param mContext              - the android context
     */
    public OpenHelper(Context mContext) {
        super(mContext, DATABASE_NAME, TARGET_VERSION);
    }
}


Finally, we have to modify the BankAccoutn RowMapper. The developer is responsible how to resolve these references, normally You want lazy loading, so this is our extension :
package org.baracus.dao;

// ...

public class BankAccountDao extends BaseDao<BankAccount> {

    @Bean
    CustomerDao customerDao;

    // ..

    private final RowMapper<BankAccount> rowMapper = new RowMapper<BankAccount>() {
        @Override
        public BankAccount from(Cursor c) {
            
            // ...

            Long customerId = c.getLong(customerIdCol.fieldIndex);
            result.setCustomerReference(new LazyReference<Customer>(new ReferenceLoader<Customer>(customerDao, customerId)));

            result.setTransient(false);
            return result;        
        }
        // ...

        @Override
        public ContentValues getContentValues(BankAccount account) {
            // ...

            if (account.getCustomerReference() != null && account.getCustomerReference().getObjectRefId() != null) {
                result.put(customerIdCol.fieldName, account.getCustomerReference().getObjectRefId());
            }
            return result;
        }
    };
    // ...
}
Because all ID-columns in the Baracus persistence world, You can simplify the lazy loading by passing a ReferenceLoader to the lazy reference. That's all, the resolution of the object now can be done lazily.

I am fully conscious about the fact, that this solution is technologically far behind a JPA implementation like hibernate, but - and I believe this as a full time JEE programmer using JPA all the day - leaving more control to the programmer in a small scale world like android is much better and efficient than making use of reflection and mapping code generation. If you need more transparency, You might inherit the reference class encapsulating all the specialized code for relation resolution. Maybe I am going to make another tutorial about that, later.


Step 3 : One-To-Many relation


Now lets proceed adding the (more trivial case) of the OneToMany relationship (Customer -- *BankAccount)

Therefore we add the Collection<BankAccount> plus getter/setter to the class :

public class Customer extends ModelBase {
// ...
    private String lastName;
    private String firstName;
    private Collection<BankAccount> accounts;
// ...
    public Collection<BankAccount> getAccounts() {
        return accounts;
    }

    public void setAccounts(Collection<BankAccount> accounts) {
        this.accounts = accounts;
    }
}

In order to make the stuff beeing filled with life, we have to write a DAO function retrieving the BankAccount list for each customer :


package org.baracus.dao;
// ...
public List<BankAccount> getByCustomerId(Long id) {
            Cursor c = null;
            List<BankAccount> result = new LinkedList<BankAccount>();
            try {
                c = this.getDb().query(true, rowMapper.getAffectedTable(), rowMapper.getFieldList().getFieldNames(), BankAccount.customerIdCol.fieldName + "=" + id.toString(), null, null, null, null, null);
                result = iterateCursor(c);
            } finally {
                if (c != null && !c.isClosed()) {
                    c.close();
                }
            }

            return result;

    }

Here the FieldList paired with the rowmapper does the job of making the matching row columns to fields more easy. Having a little comfort like this was the initial intention of the OR technique in this framework.

Finally, we extend the Customer's RowMapper implementation in order to get the relation resolved :


package org.baracus.dao;

// ...

import static org.baracus.model.Customer.*;
import static org.baracus.orm.ModelBase.idCol;

// ...

public class CustomerDao extends BaseDao<Customer> {

    @Bean
    BankAccountDao bankAccountDao;

    // ...

    private final RowMapper<Customer> rowMapper = new RowMapper<Customer>() {
        @Override
        public Customer from(Cursor c) {
            Customer result = new Customer();
            final Long id = c.getLong(idCol.fieldIndex);
            result.setId(id);
            result.setLastName(c.getString(lastNameCol.fieldIndex));
            result.setFirstName(c.getString(firstNameCol.fieldIndex));
            result.setTransient(false);

            result.setAccounts(new LazyCollection<BankAccount>(new LazyCollection.LazyLoader<BankAccount>() {
                @Override
                public List<BankAccount> loadReference() {
                    return bankAccountDao.getByCustomerId(id);
                }
            }));


            return result;
        }

        // ...

    };

    // ...

}

Done, now we can lazily load the BankAccount entities per customer. This method also leaves all control to the coder. If You want to use a cache implementation instead of the dao, You easily can implement a LazyLoader doing what you want. Because the Container Types in Java are used, there is no Reference-alike solution needed.

 Now that we have created a bidirectional relationship, let's try to use it!

Finish - tryout the relationship

In order to demonstrate the lazy stuff inside of our model, we are going to add some code to the MainActivity.


package org.baracus;

// ...

public class HelloAndroidActivity extends Activity {

    static final Logger logger = new Logger(HelloAndroidActivity.class);

    static {
        Logger.setTag("TUTORIAL_APP");
    }

    @Bean
    CustomerService customerService;

    @Bean
    BankAccountService bankAccountService;

    @Bean
    ConfigurationService configurationService;

    @Bean
    CustomerDao customerDao;

    @Bean
    BankAccountDao bankAccountDao;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
          
        if (!configurationService.isApplicationInitializationDone()) {
            initData();
            configurationService.setApplicationInitializationDone(true);
        }
    }

    private void initData() {
        Customer johnDoe = new Customer();
        johnDoe.setFirstName("John");
        johnDoe.setLastName("Doe");
        customerDao.save(johnDoe);

        BankAccount b1 = new BankAccount();
        b1.setCustomer(johnDoe);
        b1.setIban("1234DOE777");
        b1.setBankName("Foo Bank Inc.");

        BankAccount b2 = new BankAccount();
        b2.setCustomer(johnDoe);
        b2.setIban("DOEDEEDOE");
        b2.setBankName("Bar Investments Inc.");

        bankAccountDao.save(b1);
        bankAccountDao.save(b2);

    }
    
    // ...

    public void onButtonTestClicked(View v) {

        // Demonstrate Customer 1:N Lazy Loading
        List<Customer> allCustomers = customerDao.loadAll();

        for (Customer customer : allCustomers) {
            List<BankAccount> customerAccounts = customer.getAccounts();
            for (BankAccount account : customerAccounts) {
                logger.info("Customer $1 $2 --- account --> $3/$4", customer.getFirstName(),customer.getLastName(), account.getBankName(), account.getIban());
            }
        }

        // Demontrate BankAccount N:1 Lazy Loading
        List<BankAccount> allBankAccounts = bankAccountDao.loadAll();
        for (BankAccount account : allBankAccounts){
            logger.info("Bank Account $1 / $2 --- customer ---> $1 $2", account.getBankName(), account.getIban(), account.getCustomer().getFirstName(), account.getCustomer().getLastName());
        }
    }

}

As You can see, the data shall become initialized once. Notice, if You should remove the prior installation of this software version before proceeding! Otherwise You'll run into a NullPointerException! Also, be sure, that all Your beans (Dao, Services etc) are registered in the application context!

The persistence layer of the Baracus framework needs manually written persistence code; a JPA enviroment doesn't. In a normal situation You will not have hundreds of database tables and not an ultra-complex object model in a mobile app. That's one of the reasons, why nobody attempted to bring glassfish onto Your smartphone. This closeness to the database using the basic featureset of the framework enables You to act close to the database saving a lot of performance. Baracus is not database aware, it embraces the SQLite built in every android device and lets You built very performant persistence layers with a sustainable pain on mapping the object ONCE. The result can be a set of smart persistence objects making Your developer life a little easier. 


Follow Up : Baracus approach for automatic form validation

Mittwoch, 23. Oktober 2013

BARACUS from Scratch - Part 4 - IOC and DI

Table of Contents

Previous Tutorial : Basic Persistence Mapping

In this tutorial I am going to give You a brief overview about the IOC and DI implementation of the Baracus framework. With few interfaces ond even fewer effort You can make Your application context to the central instance of management for Your beans and components.

 Download the sourcecode of this tutorial from Github

 Step 1 - Dependency Injection 

Dependency Injection (or shortened DI) is the most common and most well known aspect of IOC. IOC itself stands for Inversion of Control and is basically summarizeable as the "Hollywood principle" in the Java bean world. The hollywood priciple says "Don't call us, we'll call You".

Basically, Dependency Injection means to us, that in our world no bean constructors are called explicitly. Instead of creating a bean instance, the instance of a bean is created by the bean container and brought into our bean via injection.

Beans are instantiated by the ApplicationContext


To make use of CDI and DI, You have to register Your Java class as a container managed bean. If you followed the prior tutorials, You already made that when You registered Your DAOs - the approach Baracus follows disregards anything bound to dynamic annotation scanning, so You have to call the registerBeanClass function providing Your bean class to the Baracus Application container. That's it. Now Your bean a) is instantiated and managed by Baracus and b) automatically has become injecteable to other beans.

Baracus brings You a ConfigurationDao bean by default, which enables You to store and retrieve key-value-pairs for Your config options. It is very sensible to encapsulate this into a ConfigurationService, so here we start :

package org.baracus.service;

import org.baracus.annotations.Bean;
import org.baracus.dao.ConfigurationDao;
import org.baracus.model.ConfigurationParameter;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */

@Bean
public class ConfigurationService {

    @Bean
    ConfigurationDao configurationDao;

}


After registering this class, the configurationDao will be injected automatically, that means, we don't have to call any contructors. As you can see, the ConfigurationDao and the service class both are annotated with the @Bean annotation.

This annotation is only for documentation purpose! The injection is done on the type of the bean class!

After creating the service bean, we need to register it in the ApplicationContext:

package org.baracus.application;

// ....

public class ApplicationContext extends BaracusApplicationContext {

    static {
        // ....
        registerBeanClass(ConfigurationService.class);
    }   

}

That's it. Finally let us place some sensibe function to demonstrate the encapsulation of a configuration parameter into the bean:


package org.baracus.service;

import org.baracus.annotations.Bean;
import org.baracus.dao.ConfigurationDao;
import org.baracus.model.ConfigurationParameter;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */

@Bean
public class ConfigurationService {

    @Bean
    ConfigurationDao configurationDao;

    private static final String KEY_APP_INIT_DONE="APPL_INIT_DONE";

    private static enum YesNo {
        YES,
        NO
    }

    public boolean isApplicationInitializationDone() {
        ConfigurationParameter parameter= configurationDao.getByName(KEY_APP_INIT_DONE);
        if (parameter != null) {
            return YesNo.YES.toString().equals(parameter.getConfigParameterValue());
        }
        return false;
    }

    public void setApplicationInitializationDone(boolean isEnabled) {
        ConfigurationParameter parameter= configurationDao.getByName(KEY_APP_INIT_DONE);

        if (parameter == null) {
            parameter = new ConfigurationParameter();
            parameter.setConfigParameter(KEY_APP_INIT_DONE);
        }

        parameter.setConfigParameterValue(isEnabled ? YesNo.YES.toString()
                                                    : YesNo.NO.toString());

        configurationDao.save(parameter);

    }
}

That's all. Now we are able to create some "first-run-logics" when the Application is launched the very first time on a device.

DI : Conclusion

Baracus offers You a very simple - but lightweight - type based dependency injection, which has got certain limitations :

  1. Discrete Types only. You can use inheritance in Your beans, but You always must register a discrete type in order to have dependency injection. The Baracus implementation is very simple and lightweight and does not support complex qualifiers like CDI (JSR 330) implementations like spring.
  2. No polymorphisms, You cannot register a bean implementation twice!
  3. Default constructors; Your bean must a) provice a default constructor - or - a constructor taking the context.
Therefore, you get some powerful feautures :
  1. You have dependency injection available in any Activity
  2. You have dependency injection available in any implementation of ManagedFragment. You should not use the standard fragment, because Android has the very painful behaviour on re-instantiating fragment internally on a device rotate. ManageFragment takes care of the DI part.
  3. You even can have circular dependencies - although a good design should avoid this.
  4. In good old times (tm) everything was coupled using interfaces. Since JEE 6 this paradigm has changed, so reducing the injection to the most basic information (=the class) is imo a quite effective way.

Step 2 - Making use of Lifecycle Management

Sometimes You need run some code in order to make a component usable to others. A good example is a cache, which is built up on start and needs to be teared down on shutdown.

To get suport of the lifecycle management, simply implement Initializeable or the Destroyable interfaces.

The postConstruct() method will be called, after Baracus finished the bean's dependency injection and enables You to take influence on the bean initialization before delivery.

The onDestroy() method is called, before the bean is discarded and lets You take influence on the bean's disposal. I chose the name using the android function in order to avoid double implementations on form classes.

So we are having Our ConfigurationService like this :

package org.baracus.service;

import org.baracus.annotations.Bean;
import org.baracus.dao.ConfigurationDao;
import org.baracus.lifecycle.Destroyable;
import org.baracus.lifecycle.Initializeable;
import org.baracus.model.ConfigurationParameter;

import java.util.Date;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */

@Bean
public class ConfigurationService implements Initializeable, Destroyable{

    @Bean
    ConfigurationDao configurationDao;

    Date lastStarted;

    private static final String KEY_APP_INIT_DONE="APPL_INIT_DONE";
    private static final String KEY_APP_LAST_START="APPL_LAST_START";

    private static enum YesNo {
        YES,
        NO
    }

    public boolean isApplicationInitializationDone() {
        ConfigurationParameter parameter= configurationDao.getByName(KEY_APP_INIT_DONE);
        if (parameter != null) {
            return YesNo.YES.toString().equals(parameter.getConfigParameterValue());
        }
        return false;
    }

    public void setApplicationInitializationDone(boolean isEnabled) {
        ConfigurationParameter parameter= configurationDao.getByName(KEY_APP_INIT_DONE);

        if (parameter == null) {
            parameter = new ConfigurationParameter();
            parameter.setConfigParameter(KEY_APP_INIT_DONE);
        }

        parameter.setConfigParameterValue(isEnabled ? YesNo.YES.toString()
                                                    : YesNo.NO.toString());

        configurationDao.save(parameter);

    }


    @Override
    public void onDestroy() {
        ConfigurationParameter parameter= configurationDao.getByName(KEY_APP_LAST_START);

        if (parameter == null) {
            parameter = new ConfigurationParameter();
            parameter.setConfigParameter(KEY_APP_LAST_START);
        }

        parameter.setConfigParameterValue(String.valueOf(lastStarted.getTime()));

        configurationDao.save(parameter);

    }

    @Override
    public void postConstruct() {
        lastStarted = new Date();
    }

}

Notice, the destruction function is not called by android natively on exit. You need to wire it explicitly atm.

Finish : Demonstrating the lifecycle


To demonstrate bean container shutdown and startup, I modified the button callback :

package org.baracus;

// ...

public class HelloAndroidActivity extends Activity {

    // ...

    public void onButtonTestClicked(View v) {
        customerService.testService();
        bankAccountService.createAndOrDumpAccount();
        ApplicationContext.destroy(true);
        ApplicationContext.initApplicationContext();
    }

}


That's all. Now how are able to manage bean lifecycles with the container :)

Next Tutorial : Advanced persistence mapping

Sonntag, 20. Oktober 2013

Version 0.6 released

Version 0.6 stable now is released.

It contains several bugfixes and introduces an extensible validation system. Sources can be found on Github.

A quick demo for the bean validation can be found in the Demonstration Application

A tutorial is going to follow, soon.

(mnt)

Samstag, 19. Oktober 2013

BARACUS from Scratch : Part 3 - Persistence Mapping Basics

Table of Contents

Previous Tutorial : Dependency Injection and IOC

In this tutorial I am going to explain You the basic fundamentals You need to know for storing and loading data in the application's database. There are four steps necessary to define an entity after defining the OpenHelper : Define the entity class and wire the fields, define a DAO taking care of the mapping, define a migr8-Implementation creating the table and add it to the migration steps associated with a step number indicating the target database version.

Download the sourcecode to this tutorial from Github

Step 1 : The OpenHelper

If You followed the tutorial from the prior chapter, You can skip this step. Otherwise, just follow the instructions of this step.

You need an Implementation of the BaracusOpenHelper to manage Your database. Simply derive the class and fit it with a constructor taking the Android context. The other two constructor parameters need to be set by Your implementation and will define the database filename and the target version of the database:


/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */
public class OpenHelper extends BaracusOpenHelper {

    private static final String DATABASE_NAME="tutorial-app.db";
    private static final int TARGET_VERSION=100;

    /**
     * Open Helper for the android database
     *
     * @param mContext              - the android context
     */
    public OpenHelper(Context mContext) {
        super(mContext, DATABASE_NAME, TARGET_VERSION);
    }
}

This class needs to be registered to the application context :

package org.baracus.application;

import org.baracus.context.BaracusApplicationContext;
import org.baracus.dao.BaracusOpenHelper;
import org.baracus.service.CustomerService;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */
public class ApplicationContext extends BaracusApplicationContext {

    static {
        registerBeanClass(OpenHelper.class);
        registerBeanClass(CustomerService.class);
    }

}

That's all. Now we can proceed to the next step, defining the entity class.

Step 2 : Defining the entity class

To keep the entity management as easy as as possible, all entity classes derive from ModelBase class. The first constant You'll define will be the table name inside of the database:


package org.baracus.model;

import org.baracus.orm.ModelBase;
import org.baracus.orm.Field;
import org.baracus.orm.FieldList;

import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */
public class BankAccount extends ModelBase {

    public static final String TABLE_BANK_ACCOUNT = "bank_account";


Every persistent class has an indexed list of all fields, in hibernate terms you would call this metadata. The major difference is, that the metadata is held statically inside of the persistence class. Therefore You need to define a Fieldlist carrying the type. To inherit the primary key surrogate field (a generated, unique Long number), You must add the superclass's FieldList to the classses field list and then add the class fields. This stuff should be static; using static and final is a major performance thing inside of a mobile devices. To make the indexing easier, I use an iterator int to keep on track:


public class BankAccount extends ModelBase {

    public static final String TABLE_BANK_ACCOUNT = "bank_account";

    private static int columnIndex= ModelBase.fieldList.size();

    private String bankName;
    private String iban;

    public static final FieldList fieldList = new FieldList(BankAccount.class.getSimpleName());
    public static final Field bankNameCol = new Field("bank_name", columnIndex++);
    public static final Field ibanCol = new Field("iban", columnIndex++);


    static {
        fieldList.add(ModelBase.fieldList);
        fieldList.add(bankNameCol);
        fieldList.add(ibanCol);
    }


As You can see, I added the bank name and the international bank number to my entity class. Then, I define the fields (all static final) and then add the fields inside of a static init block to the fields.

Finally, I define getters and setters and my entity definition is ready to use:


package org.baracus.model;

import org.baracus.orm.ModelBase;
import org.baracus.orm.Field;
import org.baracus.orm.FieldList;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */
public class BankAccount extends ModelBase {

    public static final String TABLE_BANK_ACCOUNT = "bank_account";

    private static int columnIndex= ModelBase.fieldList.size();

    private String bankName;
    private String iban;

    public static final FieldList fieldList = new FieldList(BankAccount.class.getSimpleName());
    public static final Field bankNameCol = new Field("bank_name", columnIndex++);
    public static final Field ibanCol = new Field("iban", columnIndex++);


    static {
        fieldList.add(ModelBase.fieldList);
        fieldList.add(bankNameCol);
        fieldList.add(ibanCol);
    }

    public BankAccount() {
        super(TABLE_BANK_ACCOUNT);
    }

    public String getBankName() {
        return bankName;
    }

    public void setBankName(String bankName) {
        this.bankName = bankName;
    }

    public String getIban() {
        return iban;
    }

    public void setIban(String iban) {
        this.iban = iban;
    }

}
That's it.

Step 3 : Defining the Table

If You want to store and load data from the database, You have to create a table inside of the app's DB. Therefore, You'll have to define a MigrationStep. For the beginning, let us simply define a single migration step for the single table we have.

In a later tutorial, I am going to explain You migr8 and the concept of database model versioning detailled.

So, in order to keep the java model and the database model synchronously and to avoid naming clashes, we simply reuse the table and the column name identifiers.

Our database metamodel class (which is an implementation of MigrationStep)  is going to look like this :

package org.baracus.migr8;

import android.database.sqlite.SQLiteDatabase;
import org.baracus.model.BankAccount;
import org.baracus.util.Logger;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 * To change this template use File | Settings | File Templates.
 */
public class ModelVersion100 implements MigrationStep {

    private static final Logger logger = new Logger(ModelVersion100.class);

    @Override
    public void applyVersion(SQLiteDatabase db) {

        String stmt  = "CREATE TABLE " + BankAccount.TABLE_BANK_ACCOUNT
                + "( "+ BankAccount.idCol.fieldName+" INTEGER PRIMARY KEY"
                + ", "+ BankAccount.bankNameCol.fieldName+ " TEXT"
                + ", "+ BankAccount.ibanCol.fieldName+ " TEXT"+
                  ")";
        logger.info(stmt);
        db.execSQL(stmt);

    }

    @Override
    public int getModelVersionNumber() {
        return 100;
    }
}

For myself, I discovered starting with version 100 (=1.0.0) as a good common sense, because it allows you to have your model versioned having fixed length (=3 chars) integer number with much space for migration (100 <= version < 1000).  

You can individually choose Your numbering; the only thing You have to regard, is the fact, that Your model version number increases with every new model.
Now we have the database ddl section covered. To make use of it, we register it in the OpenHelper using the addMigrationStep function. The implementation of the getModelVersionNumber function will take care of the correct order of the steps:

package org.baracus.application;

import android.content.Context;
import org.baracus.dao.BaracusOpenHelper;
import org.baracus.migr8.ModelVersion100;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */
public class OpenHelper extends BaracusOpenHelper {

    private static final String DATABASE_NAME="tutorial-app.db";
    private static final int TARGET_VERSION=100;

    static {
        addMigrationStep(new ModelVersion100());
    }

    /**
     * Open Helper for the android database
     *
     * @param mContext              - the android context
     */
    public OpenHelper(Context mContext) {
        super(mContext, DATABASE_NAME, TARGET_VERSION);
    }
}

Finally, we have to define a DataAccessObject in order to access the entity.

Step 4 : Defining the DAO

In order to access Your data, the DAO pattern (Data Access Object) is a prooved way to perform database read and write operation. The Baracus ORM leverages DAO as the standard way for db operations.

Notice : Be sure, that the bean has got a public default constructor!

Therefore you have to inherit and implement a BaseDao. The BaseDao is a generic which needs to be parameterized :


package org.baracus.dao;

import org.baracus.model.BankAccount;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */
public class BankAccountDao extends BaseDao {

    /**
     * Lock the DAO of
     */
    public BankAccountDao() {
        super(BankAccount.class);
    }

    @Override
    public RowMapper getRowMapper() {
        return null; // TODO : implement
    }
}
Finally, You have to provide a RowMapper implementation telling the Dao how to map data to objects and vice versa. Let us start the most simple methods :

getAffectedTable simply returns the BankAccount's table constant, getFieldList returns the BankAccount's FieldList constant.

If an entity has got a name attribute, You can return the column name here and be able to make use of the DAO's getByName function. So the RowMapper implementation (notice, static final and inline) looks like this :


package org.baracus.dao;

import android.content.ContentValues;
import android.database.Cursor;
import org.baracus.model.BankAccount;
import org.baracus.orm.Field;
import org.baracus.orm.FieldList;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 * To change this template use File | Settings | File Templates.
 */
public class BankAccountDao extends BaseDao<BankAccount> {

    /**
     * Lock the DAO of
     */
    protected BankAccountDao() {
        super(BankAccount.class);
    }

    private static final RowMapper<BankAccount> rowMapper = new RowMapper<BankAccount>() {
        @Override
        public BankAccount from(Cursor c) {
            return null;  //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public String getAffectedTable() {
            return BankAccount.TABLE_BANK_ACCOUNT;
        }

        @Override
        public FieldList getFieldList() {
            return BankAccount.fieldList;
        }

        @Override
        public Field getNameField() {
            return BankAccount.bankNameCol;
        }

        @Override
        public ContentValues getContentValues(BankAccount item) {
            return null;  //To change body of implemented methods use File | Settings | File Templates.
        }
    };

    @Override
    public RowMapper<BankAccount> getRowMapper() {
        return rowMapper;
    }
}


The crucial - and still missing - part of the class is the mapping itself. Here we have to define how the class is mapped from a row and back. Mapping the row to an object is done on the cursor object.

package org.baracus.dao;

import android.content.ContentValues;
import android.database.Cursor;
import org.baracus.model.BankAccount;
import org.baracus.orm.Field;
import org.baracus.orm.FieldList;
import org.baracus.orm.LazyCollection;

import java.util.List;

import static org.baracus.model.BankAccount.bankNameCol;
import static org.baracus.model.BankAccount.ibanCol;
import static org.baracus.orm.ModelBase.idCol;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 * To change this template use File | Settings | File Templates.
 */
public class BankAccountDao extends BaseDao<BankAccount> {

    /**
     * Lock the DAO of
     */
    protected BankAccountDao() {
        super(BankAccount.class);
    }

    private static final RowMapper<BankAccount> rowMapper = new RowMapper<BankAccount>() {
        @Override
        public BankAccount from(Cursor c) {
            BankAccount result = new BankAccount();
            result.setId(c.getLong(idCol.fieldIndex));
            result.setIban(c.getString(ibanCol.fieldIndex));
            result.setBankName(c.getString(bankNameCol.fieldIndex));
            result.setTransient(false);
            return result; 
}

        @Override
        public String getAffectedTable() {
            return BankAccount.TABLE_BANK_ACCOUNT;
        }

        @Override
        public FieldList getFieldList() {
            return BankAccount.fieldList;
        }

        @Override
        public Field getNameField() {
            return bankNameCol;
        }

        @Override
        public ContentValues getContentValues(BankAccount account) {
            ContentValues result = new ContentValues();
            if (account.getId() != null) { result.put(idCol.fieldName, account.getId()); }
            if (account.getIban() != null) { result.put(BankAccount.ibanCol.fieldName, account.getIban()); }
            if (account.getBankName() != null) { result.put(bankNameCol.fieldName, account.getBankName()); }
            return result;
        }
    };

    @Override
    public RowMapper<BankAccount> getRowMapper() {
        return rowMapper;
    }
}

Notice, since Baracus determines the DB insert and update operation out of the transient field, it is of VITAL IMPORTANCE, that the last call in Your from()-function is setTransient(false)

As You can see, the mapping is quite simple; the mapping from the cursor simply wires the field using the class field's field indexes in order to determine the correct position.

Finally, we have to register the dao bean in the application context:


package org.baracus.application;

import org.baracus.context.BaracusApplicationContext;
import org.baracus.dao.BankAccountDao;
import org.baracus.dao.BaracusOpenHelper;
import org.baracus.service.CustomerService;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */
public class ApplicationContext extends BaracusApplicationContext {

    static {
        registerBeanClass(OpenHelper.class);

        registerBeanClass(BankAccountDao.class);
        registerBeanClass(CustomerService.class);
    }

}

That's all. In a later tutorial, I am going to show You how to use references and lazy collections in order to make Your model navigable.

I am completely conscious about the fact, that this is far away from the comfort of an implementation of Java Persistence Architecture. I had two major requirements to my ORM : a) no reflection if possible and b) no generated code. There exist frameworks for android supporting this, but I chose this way for the sake of control about all Your operations. However, You normally never have to map an entity never and mobile applications are far away from having dozens of database tables ;-)

Finish : Creating and retrieving data

To proof the DAO functionality, We are going to create and retrieve an Account item. Therefore we create and register the service class BankAccountService carry a createOrDumpAccount method.

So the service is actually looking like this :


package org.baracus.service;

import org.baracus.annotations.Bean;
import org.baracus.dao.BankAccountDao;
import org.baracus.model.BankAccount;
import org.baracus.util.Logger;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */
@Bean
public class BankAccountService {

    private static final Logger logger = new Logger(BankAccountService.class);

    @Bean
    BankAccountDao bankAccountDao;

    public void createAndOrDumpAccount() {
        BankAccount account = bankAccountDao.getByName("FOOBANK");
        if (account == null) {
            logger.info("No account for FOOBANK was found. I am going to create one.");
            account = new BankAccount();
            account.setBankName("FOOBANK");
            account.setIban("MARMELADEFOO666");
            bankAccountDao.save(account);
        } else {
            logger.info("ACCOUNT FOUND. Id is $1",account.getId());
            logger.info("String value $1",account.toString());
        }
    }
}



and we are going to wire it to the button function in the MainActivity :
// ...
public class HelloAndroidActivity extends Activity {

   // ...

    @Bean
    BankAccountService bankAccountService;

   // ...

    public void onButtonTestClicked(View v) {
        customerService.testService();
        bankAccountService.createAndOrDumpAccount();
    }

}


... done! Now You can watch the results in Your logfile :


10-18 20:42:34.260: DEBUG/TUTORIAL_APP(12699): CustomerService Hooray! I have been called!
10-18 20:42:34.260: DEBUG/TUTORIAL_APP(12699): BankAccountService No account for FOOBANK was found. I am going to create one.

... second click ...

10-18 20:42:54.630: DEBUG/TUTORIAL_APP(12699): CustomerService Hooray! I have been called!
10-18 20:42:54.630: DEBUG/TUTORIAL_APP(12699): BankAccountService ACCOUNT FOUND. Id is 1
10-18 20:42:54.630: DEBUG/TUTORIAL_APP(12699): BankAccountService String value ModelBase{id=1, isTransient=false, tableName='bank_account'}

Now You can define any persistent class You want. 

One final word to the four magic steps (Model, DDL, Dao, Wire+Use)... If you have a release version of Your application, DO NOT ALTER ANY MIGRATION STEPS! All modifications to the database model HAVE to be done via a new MigrationStep implementation containing the matching ALTER TABLE's!

Follow Up : Dependency injection and IOC

Sonntag, 13. Oktober 2013

BARACUS from Scratch : Part 2 - Defining the application context and making use of Dependency Injection (DI+IOC)

Table of Contents

Previous Tutorial : Setting up the Application

In this tutorial I am going to show You how to launch the application under control of the Baracus Framework, how to define basic beans, how to inject them and how to take control of the bean's lifecycle.

Download the sourcecode of this tutorial from Github

NOTICE

This tutorial is intended to understand how the wiring is done. If you just want to use the framework, simply run a mvn archetype:generate and search for BARACUS. An archetype containing an working example configuration has been released recently to ease the use of the framework.



Step 1 : Make BARACUS to Your application context

In order to make the BARACUS Application Context to the android application context, You have to inherit the BaracusApplicationContext class.

In case of Android version 3.2,  you must leave the SDK-Level in the AndroidManifest.xml at 14. You can use this libs in a Version 13 level environment, if You do not call the Level 14 functions. A V32 Compatibility lib is planned but not available yet. You might run into problems!

In this class, You have to register all bean classes manually. I chose this strategy in order to avoid code generation or excessive use of reflection inside of the application context. So basically, Your ApplicationContext looks like this after the first step :


package org.baracus.application;
import org.baracus.context.BaracusApplicationContext;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */
public class ApplicationContext extends BaracusApplicationContext {

}

So far, so good, but this class needs to be registered as "the" application management class in the AndroidManifest.xml. Android supports the android:name attribute for this. So You simply add it to the application-tag, carrying the classname without the packagename

android:name=".application.ApplicationContext"

which leads us to this AndroidManifest.xml :


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.baracus"
    android:versionCode="1"
    android:versionName="1.0-SNAPSHOT" >

    <uses-sdk
        android:minSdkVersion="13"
        android:targetSdkVersion="16" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:name=".application.ApplicationContext"
        android:theme="@style/AppTheme">
        <activity android:name=".HelloAndroidActivity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

That's it. Now the Baracus Framework will be the registered Application Context for the android app. If You are using Android 3.2 You need to the lifecycle management manually.

Step 2 : The OpenHelper

Because Baracus also supports the lifecycle of Your database, You must implement an OpenHelper, even if You do not have any database support (You implicitly will have a database because of the ConfigurationDao inside of the framework). You have to define two vital information elements there :

The name of the application database and the target version.

This can be easily done like this :

package org.baracus.application;

import android.content.Context;
import org.baracus.dao.BaracusOpenHelper;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */
public class OpenHelper extends BaracusOpenHelper {

    private static final String DATABASE_NAME="tutorial-app.db";
    private static final int TARGET_VERSION=100;

    /**
     * Open Helper for the android database
     *
     * @param mContext              - the android context
     */
    public OpenHelper(Context mContext) {
        super(mContext, DATABASE_NAME, TARGET_VERSION);
    }
}
This class needs to be registered in Your ApplicationContext, see below for details. In a later tutorial, I will explain how to use the OpenHelper for supporting consistent database handling and migration.

Step 3 : Define and register Your first beans

Now we can start defining bean classes managed by Baracus. This is much more easier than You might think and is done in two simple steps : 1. Define class, 2. Register the class in Your ApplicationContext.

At first, we create a simple Java service class containing a logger and some simple output function :

@Bean
public class CustomerService {

    private static final Logger logger = new Logger(CustomerService.class);

    public void testService() {
        logger.info("Hooray! I have been called!");
    }
}

This is really simple. Now we have to register this bean as a service bean in Our application context, which makes Our ApplicationContext looking like this :
package org.baracus.application;

import org.baracus.context.BaracusApplicationContext;
import org.baracus.service.CustomerService;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 */
public class ApplicationContext extends BaracusApplicationContext {

    static {
        registerBeanClass(OpenHelper.class);
        registerBeanClass(CustomerService.class);
    }

}
Now, we are able to auto-inject this bean into any managed component. Because Baracus is the ApplicationContext of Our application, we easily are able to inject the bean by referencing the type in the HelloWorldActivity:

package org.baracus;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import org.baracus.annotations.Bean;
import org.baracus.service.CustomerService;

public class HelloAndroidActivity extends Activity {

    @Bean
    CustomerService customerService;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
 getMenuInflater().inflate(org.baracus.R.menu.main, menu);
 return true;
    }
Please notice, the @Bean annotation is only for documentation purpose and has no runtime retention! The injection mechanism is based on the type of a class! This is a very efficient way of dealing with classes, but it disallows messing around with complex type hierarchies. The bean class always must be an instantiatable POJO!

There always already exists some beans inside of Your container, the ConfigurationDao, which allows You to store configuration tokens into the applications database (key-value), the ValidationFactory used for form validation, and the ErrorHandlingFactory for routing errors to the form components, but these beans are going to be explained mored detailed in a later tutorial.

Step 4 : Bean lifecycle

Baracus allows You to define lifecylce functions; You may define a initialization and a destruction function. This functions are close to the JEE @PostConstruct and @PreDestroy functions, but are basing on interfaces. They bring the concept of IOC (Inversion of Control) into the Baracus Framework and are used to make init code easier and manageable by the container. Notice, You normally never call these function by Yourself! They are called by Baracus BeanContainer (IOC, You know?) In order to influence the lifecycle of, let Your bean simply implement Initializeable (for Post-Construct-alike behaviour) or Destroyable (for Pre-Destroy-alike behaviour) :

package org.baracus.service;

import org.baracus.annotations.Bean;
import org.baracus.dao.ConfigurationDao;
import org.baracus.lifecycle.Destroyable;
import org.baracus.lifecycle.Initializeable;
import org.baracus.util.Logger;

/**
 * Created with IntelliJ IDEA.
 * User: marcus
 * Date: 13.10.13
 * Time: 15:06
 * To change this template use File | Settings | File Templates.
 */
@Bean
public class CustomerService implements Initializeable, Destroyable {

    private static final Logger logger = new Logger(CustomerService.class);

    @Bean
    ConfigurationDao configurationDao;

    public void testService() {
        logger.info("Hooray! I have been called!");
    }

    @Override
    public void onDestroy() {
        logger.info("Bean destruction initiated!");
    }

    @Override
    public void postConstruct() {
        logger.info("Bean is created. Is the ConfigurationDao correctly injected : $1", (configurationDao != null));
    }
}


Finish : The first real usage

Finally, we add some button to the form in order to have some application feedback. At first, we create the button in the activity_main.xml :

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" android:id="@+id/textView"/>

    <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Test"
            android:id="@+id/button" android:layout_alignRight="@+id/textView" android:layout_alignParentTop="true"
            android:layout_marginTop="39dp"
            android:onClick="onButtonTestClicked"/>

</RelativeLayout>

Then, we create the button callback :


package org.baracus;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import org.baracus.annotations.Bean;
import org.baracus.service.CustomerService;

public class HelloAndroidActivity extends Activity {
    static final Logger logger = new Logger(HelloAndroidActivity.class);
    static {
        Logger.setTag("TUTORIAL_APP");
    }
    @Bean
    CustomerService customerService;

    /**
     * Called when the activity is first created.
     * @param savedInstanceState If the activity is being re-initialized after 
     * previously being shut down then this Bundle contains the data it most 
     * recently supplied in onSaveInstanceState(Bundle). <b>Note: Otherwise it is null.</b>
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
 // Inflate the menu; this adds items to the action bar if it is present.
 getMenuInflater().inflate(org.baracus.R.menu.main, menu);
 return true;
    }

    public void onButtonTestClicked(View v) {
        customerService.testService();        
    }

}

In the start activity I set the Logger-Tag, this makes grepping the logfile lotsa easier :) Now all prerequisites are met, we can launch the application and track the logfile
.... 10-13 16:05:49.460: DEBUG/TUTORIAL_APP(10555): CustomerService Bean is created. Is the ConfigurationDao correctly injected : true .... 10-13 16:06:01.290: DEBUG/TUTORIAL_APP(10555): CustomerService Hooray! I have been called! .... Notice, the bean's destruction message would become visible, if You explicitly shutdown the bean container. Simply exiting the app will not make the container stop, because the application will continue running in the background of Your android device.


Next Tutorial : Basic Persistence Mapping

Samstag, 12. Oktober 2013

BARACUS from Scratch : Part 1 - Setting up the application

Table of Contents

In this tutorial I am going to show You how to create a simple android project using maven and how to modify the pom.xml in order get Baracus available in Your application.


DEPRECATION NOTICE : 


THIS TUTORIAL IS ONLY NEEDED WHEN YOU WANT TO USE ANOTHER ANDROID ARCHETYPE! USE THE BARACUS MAVEN ARCHETYPE TO CREATE A PLAIN BARACUS ANDROID APPLICATION! 

SIMPLY TAKE A LOOK HERE AND FOLLOW THE GUIDE


Step 1 : create an android application from scratch

Before You can start using the framework You need to setup a project. I personally prefer Maven to manage my project dependencies and build stuff. I use

mvn archetype:generate

to create a maven managed application from scratch. Just type in "android" after the list of numbers appeared and choose an android quickstart archetype. Then configure Your maven based application by setting a group Id, an artifact Id and a version. Finally, choose the platform version. Notice, You are going to need minimum version 14 to use baracus. This includes all android platforms starting from android version 4.0.1.2

Then You can alter the API-Level and the plugin version. I set the android version to 4.0.1.2.
...
<platform.version>4.0.1.2/platform.version>
...

Finally, You must define the location, where Your android SDK is stored. Therefore You must set the android.sdk.path property in Your properties. So my pom-properties now look like this :
   
<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <platform.version>3.2</platform.version>
  <android.plugin.version>3.6.0</android.plugin.version>
  <android.sdk.path>/usr/java/adt-bundle-linux-x86_64/sdk</android.sdk.path>
                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>

If you have downloaded the matching android sdk stuff using the sdk/tools/bin/android utility, You now should be able to build the project. If You altered the API level to 13 (Honeycomb), You must move the res/values/values-v14 away in order to make the project compileable. When You are done, You have a sample project, which You can import into Your IDE (I personally use Jetbains IntelliJ, but there is no problem to use Eclipse or Netbeans instead).

Up to here, the entire proceeding is completely independent to any framework, so lets proceed adding the framework

Step 2 : Add the maven dependency for the framework

Notice : BARACUS has moved to Maven Central. You now can use the below coordinates using minimum version <version>0.7-Beta1</version> without any additional repository settings. The framework jars then will be downloaded from central repo.

Now we add the maven dependency to the BARACUS framework to our pom.xml. Afterwards, my dependencies look like this :

<dependencies>
 <dependency>
  <groupId>com.google.android</groupId>
  <artifactId>android</artifactId>
  <version>${platform.version}</version>
  <scope>provided</scope>
 </dependency>
        <dependency>
            <groupId>org.baracus</groupId>
            <artifactId>baracus-framework</artifactId>
            <version>${baracus.version}</version>
            <type>apklib</type>
        </dependency>
</dependencies>

You need to define the ${baracus.version} in Your properties. Please refer to www.baracus.org for the latest version.

Now You are done, the framework will be available to the application You are going to build. In the next tutorial is going to show You how to setup the application context.

Next Chapter 

Defining the Application Context and making use of DI+IOC