tag:blogger.com,1999:blog-23914900364827515692024-03-13T23:05:28.950-07:00BARACUS Framework BlogMNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.comBlogger30125tag:blogger.com,1999:blog-2391490036482751569.post-11525436818962996272016-01-24T23:02:00.001-08:002016-01-24T23:02:49.106-08:00BARACUS 0.9.3 has been releasedToday I am very glad to announce the release of BARACUS 0.9.3.<br />
<br />
In this release, two maintenance bugfixes have been done.<br />
<br />
To use the current apklib (in your Android application module), simply use the dependency<br />
<br />
<br />
<pre class="code" name="xml"> <dependency>
<groupId>org.baracus</groupId>
<artifactId>baracus-framework</artifactId>
<version>0.9.3</version>
<type>apklib</type>
</dependency>
</pre>
<br />
... as usual.<br />
<br />
The sourcecode has been <a href="https://github.com/gorefest/baracus-framework/tree/0.9.3-release-version">branched here</a> - the javadocs can be <a href="http://www.baracus.org/apidocs/0.9.2/">found here</a><br />
<br />
<br />
Regards,<br />
<br />
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: 'Times New Roman'; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; margin: 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 1; word-spacing: 0px;">
MNT</div>
MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-2836764966815253772016-01-01T06:26:00.002-08:002016-01-02T03:00:18.639-08:00BARACUS 0.9.2 has been releasedToday I am very glad to announce the release of BARACUS 0.9.2.<br />
<br />
In this release, the logging has been enhanced and been made more configurable.<br />
<br />
To use the current apklib (in your Android application module), simply use the dependency<br />
<br />
<br />
<pre class="code" name="xml"> <dependency>
<groupId>org.baracus</groupId>
<artifactId>baracus-framework</artifactId>
<version>0.9.2</version>
<type>apklib</type>
</dependency>
</pre>
<br />
... as usual.<br />
<br />
The sourcecode has been <a href="https://github.com/gorefest/baracus-framework/tree/0.9.2-release-version">branched here</a> - the javadocs can be <a href="http://www.baracus.org/apidocs/0.9.2/">found here</a><br />
<br />
<br />
Regards,<br />
MNT<br />
<div>
<br /></div>
MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-1383607050434443682015-09-09T11:23:00.000-07:002015-09-09T11:23:46.796-07:00NEW Android-Plugin version + Archetype UpdateToday I am very glad to announce the update of BARACUS maven archetype. Now it uses the simpligility android-maven-plugin, which is the successor of the <b>deprecated</b> jayway plugin.<br />
<br />
The new archetype is compliant to the required structure of the new plugin version.<br />
<br />
To create a new BARACUS based project, mvn archetype:generate your app and simply follow <a href="http://baracusframework.blogspot.de/2014/09/baracus-maven-archetype-released.html">the steps-to-perform-section here</a><br />
<br />
<b>IntelliJ users</b> please keep in mind to modifiy the project structure after creating the project or after migrating to the new plugin version:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-aEmkbnQKVpE/VfB0Fgx0DaI/AAAAAAAAIio/TQ4pz7S6Z7g/s1600/idea_structure_android.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="151" src="http://2.bp.blogspot.com/-aEmkbnQKVpE/VfB0Fgx0DaI/AAAAAAAAIio/TQ4pz7S6Z7g/s320/idea_structure_android.png" width="320" /></a></div>
<br />
<br />
HOW TO MIGRATE AN EXISTING PROJECT<br />
<br />
<br />
<ol>
<li>in pom.xml, set android.plugin.version to 4.3.0</li>
<li>in pom.xml, change (both plugin-management and plugins) android plugin group IDs to <span style="background-color: white; font-family: 'Courier New';">com.simpligility.maven.plugins</span></li>
<li>move <b>res </b>folder to <b>src/main/res</b></li>
<li>move <b>AndroidManifest.xml</b> to <b>/src/main/AndroidManifest.xml</b></li>
<li>if you have assets, do the same</li>
<li>eventually move your native libs too (I don't have any ones, so I can't tell you)</li>
</ol>
<div>
Finally, if you use IntelliJ IDEA, alter the project structure as shown above.</div>
<div>
<br /></div>
<div>
Have Fun,</div>
<div>
MNT</div>
MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-73577450909330551102015-06-21T12:51:00.001-07:002015-06-21T13:24:53.879-07:00BARACUS 0.9.1 has been released!Today I am very glad to announce the release of BARACUS 0.9.1.<br />
<br />
It contains some bugfixes, and now enables the user to have ManagedActivities stable after a device rotate; the device caused android 4 to have an internal construction call which had to be tangled with the bean container. Now the ManagedActivity will be able to survive a device rotation by reinjecting all beans.<br />
<br />
Also, I added a macro call to simplify yes-no-requester handling a little bit.<br />
<br />
Finally, I added the capability to deregister a delete callback safely.<br />
<br />
<br />
<br />
To use the current apklib (in your Android application module), simply use the dependency<br />
<br />
<br />
<pre class="code" name="xml"> <dependency>
<groupId>org.baracus</groupId>
<artifactId>baracus-framework</artifactId>
<version>0.9.1</version>
<type>apklib</type>
</dependency>
</pre>
<br />
... as usual.<br />
<br />
The sourcecode has been <a href="https://github.com/gorefest/baracus-framework/tree/0.9.1-release-version">branched here</a> - the javadocs can be <a href="http://www.baracus.org/apidocs/0.9.1/">found here</a><br />
<br />
<br />
<br />
Regards,<br />
MNT<br />
<br />MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-53215121418471936862015-03-21T01:20:00.002-07:002015-03-21T01:20:31.461-07:00BARACUS has been successfully tested under ANDROID 5.1I am glad to announce that the BARACUS reference application, another BARACUS based customer application and one of my own applications using the full BARACUS feature set have been successfully deployed and tested on an Android 5.1 device. There should be no known issues with the latest Android version. Please report any discoveries to the BARACUS forum on G+.
Have fun!
MNTMNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-40559670317741933772015-03-13T09:59:00.000-07:002015-03-13T09:59:47.390-07:00BARACUS Release 0.9 -The namespace flipToday I am glad to announce the release of BARACUS version 0.9.<br />
You can obtain the libary by adding these maven coordinates to the project<br />
<br />
<pre class="xml" name="code"><dependency>
<groupid>org.baracus</groupid>
<artifactid>baracus-framework</artifactid>
<version>0.9</version>
<type>apklib</type>
</dependency></pre>
<br />
<br />
<b>VERSION 0.9</b><br />
<br />
This release is dominated by the change of the BARACUS namespace from net.mantucon.baracus to org.baracus. The change can be easily performed by replacing all references plus altering the framework version - with IntelliJ IDEA it took me 10 minutes to migrate the entire project to the new namespace.<br />
<br />
Simply replace all occurences of<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">import net.mantucon.baracus</span><br />
<br />
by<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">import org.baracus</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
and all static imports<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">import static net.mantucon.baracus.</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span>
by<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">import static org.baracus.</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span>
In case that you are using ConstrainedText-Elements, you also must alter all layout XMLs.<br />
<br />
The maven archetype already is using the new packages so you can create BARACUS based applications easily using <span style="font-family: Courier New, Courier, monospace;">mvn generate:archetype </span>(see <a href="http://baracusframework.blogspot.de/2014/09/baracus-maven-archetype-released.html">this article for details</a>)<br />
<br />
Within the next weeks I will upgrade all tutorials and sample applications to use the new BARACUS namespaces.<br />
<br />
Regards,<br />
MNT<br />
<br />
<br />
<br />
<br />
<br />MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-10351636060257089452015-03-10T09:25:00.001-07:002015-03-10T09:26:13.252-07:00BARACUS now backed by CrowdcodeI am very pleased to announce, that the <a href="http://www.crowdcode.io/">Crowdcode</a> company will now back the BARACUS project. My company MANTUCON has dissolved in Crowdcode GmbH & Co. KG (I only will continue to perform trainings under the MANTUCON label).<br />
<br />
We are more people with a larger range of contacts and better capabilities to keep this framework alive and maintained.<br />
<br />
Also, I announce hereby to modify the package domains of the framework. I mischose them to run onder net.mantucon.baracus - despite this is an open source framework hosted and published under baracus.org.<br />
<br />
This will be subject of change within the next minor release or the when we will ascend to the 1.0 milestone :)<br />
<br />
So be prepared to refactor your applications (net.mantucon.baracus -> org.baracus - that's all) in near future!<br />
<br />
Cheers,<br />
MNT<br />
<br />
<br />MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-23696733157775108482014-10-12T10:19:00.000-07:002015-03-20T00:54:03.560-07:00BARACUS from Scratch - Part 9 - Using interface and implementations & context bootstrap<br />
<a href="http://baracusframework.blogspot.de/p/baracus-from-scratch-table-of-contents.html"><b>Table of Contents</b></a><br />
<i><br /></i>
<i>In this tutorial I am going to explain You how to use the BARACUS framework to work with alternative Implementations of an Interface plus hot-restarting the context to make use of them. Also, I will demonstrate the afterContextInitialized hook which enables You to perform post-container-startup operations.</i><br />
<i><br /></i>
Previous Tutorial : <a href="http://baracusframework.blogspot.de/2014/05/baracus-from-scratch-part-8-lifecycle.html">Lifecycle support with migr8</a><br />
<br />
You can download the sources of this tutorial <a href="https://github.com/gorefest/baracus-tutorial/tree/Tutorial-Part-8">here</a><br />
<br />
<h2>
0 - Basics</h2>
<br />
Since Version 0.8, Baracus offers the capability to register an implementation to an interface. This can be very useful in combination with the bootstrap feature (reinitialize context), because it allows You to switch between several application varieties using different implementations of the interface. The registration is simply an alternative call on BaracusApplicationContext.registerBeanClass.<br />
<br />
For this example, we will define a BankAccountLoadService, which we are going to wire twice to the BankAccountDao, logging each call on it.<br />
<br />
<h2>
1 - Defining the interface</h2>
<br />
Simply define the interface in the service package :<br />
<br />
<br />
<pre class="java" name="code">
public interface BankAccountLoadService {
List<BankAccount> loadAllAccountsByCustomerId(Long id);
}
</pre>
<br />
<br />
<br />
<h2>
2 - Implement the interface </h2>
<br />
Next, we implement the interface :<br />
<br />
<pre class="java" name="code">
public class BankAccountLoadServiceImpl1 implements BankAccountLoadService {
@Bean
BankAccountDao bankAccountDao;
final Logger logger = new Logger(this.getClass());
@Override
public List<BankAccount> loadAllAccountsByCustomerId(Long id) {
// In this demo, this is the primary implementation
logger.info("Primary implementation called!");
return bankAccountDao.getByCustomerId(id);
}
}
</pre>
<br />
<h2>
3 - Implement the alternative (dummy here)</h2>
<br />
Then we implement an alternative; this could be a test class, a webservice or whatever. Interface implementations also can be used to improve testability.<br />
<br />
<pre class="java" name="code">
/**
* Created by marcus on 30.07.14.
*/
public class BankAccountLoadServiceImpl2 implements BankAccountLoadService {
@Bean
BankAccountDao bankAccountDao;
final Logger logger = new Logger(this.getClass());
@Override
public List<BankAccount> loadAllAccountsByCustomerId(Long id) {
// In this demo, this is the alternative implementation
logger.info("Alternative implementation called!");
return bankAccountDao.getByCustomerId(id);
}
}
</pre>
<br />
<h2>
4 - Register interface</h2>
<br />
The interface now is used to register the implementation<br />
<br />
<pre class="java" name="code">
public class ApplicationContext extends BaracusApplicationContext {
static {
....
registerBeanClass(BankAccountLoadService.class, BankAccountLoadServiceImpl1.class);
}
}
</pre>
<br />
This enables the Baracus DI container to inject the implementation wherever the interface is used.<br />
<br />
<br />
<h2>
5 - Use interface for injection</h2>
<br />
Then we will pimp the RowMapper to use the implementation :<br />
<br />
<pre class="java" name="code">public class CustomerDao extends BaseDao<Customer> {
@Bean
BankAccountLoadService bankAccountLoadService;
<span class="Apple-tab-span" style="white-space: pre;"> </span>
...
private final RowMapper<Customer> rowMapper = new RowMapper<Customer>() {
@Override
public Customer from(Cursor c) {
<span class="Apple-tab-span" style="white-space: pre;"> </span> ....
result.setAccounts(new LazyCollection<BankAccount>(new LazyCollection.LazyLoader<BankAccount>() {
@Override
public List<BankAccount> loadReference() {
return bankAccountLoadService.loadAllAccountsByCustomerId(id);
}
}));
</pre>
<br />
<br />
That's all. Now we are able to use the Interface for DI. But that's not all, right? We want to make use of the ability to switch the implementation. Therefore, we will implement a ApplicationContextInitializer setting the Implementation "by random" - just for demonstration purpose. Since the decision, which implementation shall be used only happens on the boot of the app, we use a small semaphore to avoid permanent reinit. Imagine a basic context, you define and then, by configuration data (e.g. thru built-in configurationDao) you decide wether to build a thin client or a fat client - that is the one of the purposes of this class.<br />
<br />
<pre class="java" name="code">public class AfterContextInitialized implements ApplicationContextInitializer {
static boolean reinit = true;
@Override
public void afterContextIsBuilt() {
if ((new Date().getTime() % 2 ) == 0) {
ApplicationContext.registerBeanClass(BankAccountLoadService.class, BankAccountLoadServiceImpl1.class);
} else {
ApplicationContext.registerBeanClass(BankAccountLoadService.class, BankAccountLoadServiceImpl2.class);
}
if (reinit) {
reinit = false;
ApplicationContext.reinitializeContext();;
}
}
}
</pre>
<br />
This initializer also is registered in the application config :<br />
<br />
<pre class="java" name="code">public class ApplicationContext extends BaracusApplicationContext {
static {
....
setApplicationContextInitializer(new AfterContextInitialized());</pre>
<pre class="java" name="code"> }
}
</pre>
<br />
That's it, now You can check by restarting the app several time, which implementation is used by checking the logs when expanding the customer details:<br />
<br />
07-30 14:56:51.531 5316-5316/org.baracustutorial I/TUTORIAL_APP﹕ -BankAccountLoadServiceImpl1 Primary implementation called!<br />
<br />
other try :<br />
<br />
07-30 15:00:55.639 8815-8815/org.baracustutorial I/TUTORIAL_APP﹕ BankAccountLoadServiceImpl2 Alternative implementation called!<br />
<br />
<br />
<h2>
Conclusion :</h2>
<br />
As You can see, the implementation of alternatives can be used - in combination with configuration information - as a very powerful utility to create varying types of implementations / behaviour within the same codebase. The classic can be a client to be run locally using the local dao as a data service or a thin client pipelining all requests thru json requests on a remote system.<br />
<br />
Also, it can be very useful to make the software more testable.<br />
<br />
Next Tutorial :<a href="http://baracusframework.blogspot.de/2014/10/tutorial-9-writing-custom-validators.html">Writing custom validators</a><br />
<br />
<br />
<br />
<div>
<br /></div>
MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-73134752212315274292014-10-06T22:58:00.002-07:002016-01-03T08:39:53.274-08:00BARACUS from Scratch - Part 10 - Writing custom validators<a href="http://baracusframework.blogspot.de/p/baracus-from-scratch-table-of-contents.html"><b>Table of Contents</b></a><br />
<br />
Previous Tutorial :<a href="http://baracusframework.blogspot.de/2014/08/baracus-from-scratch-part-9-using.html">Using interfaces & implementations</a> <br />
<br />
You can download the sourcecode for this demo <a href="https://github.com/gorefest/baracus-tutorial/tree/Tutorial-Part-9">here</a><br />
<br />
<i>In this tutorial I will explain you how create custom validation objects to be used within your application. This enables you to have your application shipped with customized validators to fit your personal needs. This tutorial is basing on the FormValidation-Tutorial I published earlier.</i><br />
<br />
<h2>
Step 1 - Define validator</h2>
<br />
We want to add a constraint for defining names to our software. A name must start with a capital one letter and continue with zero or more small caps letters. This can be achieved by matching this java regex pattern [A-Z]+[a-z]*. So at first, we will define the constraint message plus the class:<br />
<br />
strings.xml:<br />
<br />
<pre class="xml" name="code"> <string name="nameViolation">Bad string format. Must be lowercase with starting capital letter</string>
</pre>
<br />
<br />
<pre class="java" name="code">public class NameValidator extends AbstractValidator<String>{
static final String pattern = "[A-Z]+[a-z]*"; // Will allow John but neither JOHN nor john
@Override
public boolean validate(ConstrainedView<String> view) {
return view.getCurrentValue().matches(pattern);
}
@Override
public int getMessageId() {
return R.string.nameViolation;
}
}
</pre>
<br />
<br />
<h2>
Step 2 - Register validator</h2>
<br />
You must register the validator by-name in order to be able to use it. The ValidationRegistry internally used in the BARACUS framework is a managed bean which registers the validators in a postConstruct method. But since this validator is a more or less invariant item, we will add it to the context after the context has been built. This is necessary since the ValidationFactory has not been setup in the Context-initialization phase.<br />
<br />
In a prior tutorial we have created an after-context-init trigger (AfterContextInitialized). This is the correct place to add the validator to the registry because in the ApplicationContext's init phase the internal ValidationFactory (which is also a bean) still is null.<br />
<br />
So we add the validator to the context :<br />
<br />
<pre class="java" name="code">public class AfterContextInitialized implements ApplicationContextInitializer {
...
@Override
public void afterContextIsBuilt() {
if (reinit) {
...
ApplicationContext.registerValidator(new NameValidator());
}
}
}
</pre>
<h2>
Step 3 - Wire the validator</h2>
<br />
We are almost done. You simply have to wire the validator by-name inside the desired layout xml file. In our case, we want to have this validator added to the existing validator in the customer_editor.xml :<br />
<br />
<pre class="xml" name="code"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
...
<net.mantucon.baracus.ui.ConstrainedEditText android:id="@+id/txtCustomerFirstName"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:validatedBy="stringNotEmpty,nameValidator"
/>
...
<net.mantucon.baracus.ui.ConstrainedEditText android:id="@+id/txtCustomerLastName"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:validatedBy="stringNotEmpty,nameValidator"
/>
...
</LinearLayout>
</pre>
<br />
The validators are applied in the direction you add them to the list.<br />
<br />
<h2>
Conclusion</h2>
<br />
Writing custom validators is easy. They make the app design more easy and efficient and allow you to have very specific rules beeing applied to user's form data. But that's not all. You even can have more sophisticated constraints; since you can access any bean through your ApplicationContest statically you even can have database driven checks.<br />
<br />
Next Tutorial : Advanced Logging (coming up)MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-41915885559541171262014-09-25T07:42:00.001-07:002014-09-25T23:27:05.422-07:00BARACUS version 0.8.2 releasedToday I am very glad to announce that BARACUS version 0.8.2 has been released.<br />
This minor release includes the introduction of a runtime jar file, which allows you to use BARACUS in multi-module maven projects without having colliding resources (unfortunately Android plugin assembles the framework - if included as apklib - every time it bundles the apk. This will cause multiple problems in multimodule maven projects.<br />
<br />
To avoid this problems nowadays, simple use the BARACUS runtime dependency. This enables you to create multi module maven projects using the framework.<br />
<br />
To get the framework runtime dependency simply use the dependency<br />
<br />
<pre class="code" name="xml"> <dependency>
<groupId>org.baracus</groupId>
<artifactId>baracus-framework</artifactId>
<version>0.8.2</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
</pre>
<br />
All future releases of the framework will contain the runtime dependency lib.<br />
<br />
To use the current apklib (in your Android application module), simply use the dependency<br />
<br />
<br />
<pre class="code" name="xml"> <dependency>
<groupId>org.baracus</groupId>
<artifactId>baracus-framework</artifactId>
<version>0.8.2</version>
<type>apklib</type>
</dependency>
</pre>
<br />
... as usual.<br />
<br />
<br />
Regards,<br />
MNT<br />
<br />MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-49489434846327919252014-09-07T22:35:00.000-07:002014-09-07T22:35:42.294-07:00BARACUS Maven archetype released<br />
<i>Today I am very happy to announce the release of a maven archetype to create Android applications using the BARACUS framework. It can be accessed via maven central and allows you to create application skeletons with the BARACUS framework fully integrated (no additional config needed).</i><br />
<br />
<h2>
Prerequisites</h2>
<br />
<br />
<ul>
<li>a suitable Java JDK (I still use 1.7.x atm)</li>
<li>Apache Maven Version 3 (I tend to use the latest maven distro)</li>
<li>Android SDK containing app level 14 minimum (I build my apps against level 18 atm, but that fits), since I am using linux my path is /usr/java/adt-bundle-linux-x86_64/sdk. </li>
</ul>
<h2>
Steps to perform</h2>
<br />
<br />
<ul>
<li>use <b>mvn archetype:generate</b>, ensuring that your internet connectivity is up. Then, you will get a list with 1000+ elements containing various application types. </li>
<li>Enter the term "baracus" and press enter. You now should see one element containing the BARACUS archetype</li>
<li>Enter "1" and press enter. </li>
<li>Then you will be prompted for coordinates of your app</li>
<li>Finally the maven based baracus application is created and you can open it with your favourite IDE</li>
</ul>
<br />
<h2>
<br />After creating the application</h2>
One parameter MUST be modified : You have to check the pom.xml, where a property android.sdk.path. It must point to your local android sdk. Since I am developing wih Linux, my path is /usr/java/adt-bundle-linux-x86_64/sdk. Simply modify the path your pom.xml to point to your Android SDK. If you want to develop with multiple environments, you can take advantage of maven's profiles feature. Take a look on the Baracus framework pom.xml for an example.<br />
<br />
That's it, now you can build and deploy the application on your mobile device.<br />
<br />
Features of the archetype app<br />
<br />
* context creation (ApplicationContext)<br />
* database initialization (OpenHelper)<br />
* simple data structure plus mapping (Customer+CustomerDao)<br />
* Data initialization (ApplicationInitializer Hook)<br />
* Example form validation<br />
<br />
Every release of the Baracus framework bring an update on the archetype in time.<br />
<br />
Have fun!MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-6785705998184119432014-07-25T00:02:00.000-07:002014-07-25T00:02:40.650-07:00BARACUS Release 0.8.1<br />
<br />
Hello, today I am glad to announce that BARACUS version 0.8.1 has been released. It carries several bugfixes and improvements and introduces a small amount of useful helper functions<br />
<br />
<br />
It can be included using the coordinates<br />
<br />
<pre class="xml" name="code"><dependency>
<groupid>org.baracus</groupid>
<artifactid>baracus-framework</artifactid>
<version>0.8.1</version>
<type>apklib</type>
</dependency>
</pre>
<br />
You also can download the sourcecode from <a href="https://github.com/gorefest/baracus-framework/tree/0.8.1-release-version">github</a><br />
<div>
<br /></div>
<br />
<br />
<b>VERSION 0.8.1 Changelog</b><br />
<br />
- added <b><i>DataUtil</i></b> for simple Collection -> Map transformations<br />
- added <b><i>list based delete/save functions</i></b> withing one transaction<br />
- added <b><i>Popup</i></b> macro<br />
- added generic <b><i>BaseDao.arrify</i></b> function to to make collections become an array on demand<br />
- added function to create lazy object reference (<b>BaseDao.createLazyReference</b>) by dao and id to application context (was useful to me for dto-to-model copy constructor)<br />
- added function to create lazy collection references (<i><b>BaseDao.createLazyCollection</b></i>) by dao, id and remote field<br />
(same like above)<br />
- added <b><i>support for optimistic locking</i></b>, useful when multiple android clients are writing<br />
to one android master database.<br />
- created <i><b>optmistic locking</b></i> capable entity (via version field)<br />
- created <i><b>BaseDao.initialFromCursor</b></i> factory function to make coping with ID and version<br />
a little bit more flexible.<br />
- added <b><i>NullReference</i></b> in ORM to avoid NPE handling on nullified References<br />
<br />
- added a locator function<b><i> BaracusApplicationContext.getBeansOfType</i></b> to find all instances<br />
implementing a certain class.<br />
<div>
<br /></div>
<div>
These bugs were fixed :</div>
<div>
<br /></div>
<br />
- bugfixed query-function (double iteration)<br />
- bugfixed ModelBase field list naming<br />
- bugfixed replaceBean method<br />
- bugfixed loss of timestamp info in BaseDao.save()<br />
<br />
<br />MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-78033535128327238662014-07-02T10:00:00.000-07:002014-07-03T21:13:40.414-07:00BARACUS Release 0.8<br />
Today I am glad to announce that BARACUS framework version 0.8 has been released.<br />
It can be included using the coordinates<br />
<br />
<pre class="xml" name="code"><dependency>
<groupid>org.baracus</groupid>
<artifactid>baracus-framework</artifactid>
<version>0.8</version>
<type>apklib</type>
</dependency>
</pre>
<br />
You also can download the sourcecode from <a href="https://github.com/gorefest/baracus-framework/tree/0.8-release-version">github</a><br />
<br />
A couple of features have been added:<br />
<br />
<br />
VERSION 0.8<br />
<br />
- added the getIdField function in order to make overriding the id column naming possible<br />
- added the getLoadAllCursor in order to make linking to a data DataBoundAdapter possible<br />
- to be better able to work with persistence interfaces the LazyMorphicCollection has been<br />
introduced in order to be able to return a List<Type> from a Dao<SpecialType><br />
<br />
- interface registration now possible in order to extend code reuseability<br />
additional to applicationContainer.registerBeanClass(aClass) you now can<br />
call registerBeanClass(InterfaceType, BeanClass);<br />
<br />
This is a powerful feature since it makes in-app testing much more easy.<br />
<br />
- also : replace bean implementations in the running application and context hot reinit<br />
<br />
- bugfix for NullPointerException in Android preview in IntelliJ<br />
<br />
- added support for automatic selection of modified entities (BaseDao.getAllItemsModifiedAfter)<br />
.. simply extend your rowmapper to implement TimestampSupportingRowmapper and return<br />
the timestamp fields<br />
<br />
- added an after-context-init hook to enable application post-construct<br />
- added a function deleteById carrying a Long parameter to the DAOs<br />
- added an constructor based "transient" determination to ModelBase<br />
- added some utils for basic date calculation<br />
<br />
- added queryByExample function in order to do QBE :)<br />
- modified DataSetChangeAware interface, now passes the emitted class in order to be able to<br />
create aggregator-alike DataSetChangeAware components handling multiple entity types<br />
<br />
<br />
<br />
--- DEPRECATION NOTICE ---<br />
- don't inherit AbstractModelBase any more! The "id" column clashes<br />
with the android convention of carrying an "_id" column, so I added<br />
a boolean parameter to force You making a review on Your model classes.<br />
You can either inherit LegacyModelBase (same behaviour like before) or<br />
- and this is recommended - rebuild Your tables by renaming the "id" column<br />
to an "_id" column. This restrict impacts when You try to fill view withs<br />
data using a cursor. So either You must consider using the LegacyBean<br />
or You should modify Your code.<br />
<br />
Regards,<br />
mntMNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-65256522237592671272014-07-02T08:40:00.001-07:002014-07-02T08:41:51.889-07:00Android daily pain : class R does not contain the correct constantsYesterday I started preparing the release of BARACUS 0.8. Therefore, I started to update all tutorials. By the new Android versions I surely updated my SDK in the meantime. In mind, just to do a version upgrade and use a new plugin version, I started migration.<br />
<br />
So what I did was using a newer pom, update plugin and sdk version :<br />
<br />
<pre class="xml" name="code">public class ApplicationContext extends BaracusApplicationContext {
<properties>
<android.sdk.path>C:/adt-bundle-windows-x86-20130219/sdk</android.sdk.path>
<android.plugin.version>3.8.2</android.plugin.version>
<android.sdk.version>18</android.sdk.version>
<platform.version>4.0.1.2</platform.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<baracus.version>0.8</baracus.version>
</properties>
</pre>
... and ..,
<br />
<pre class="xml" name="code">public class ApplicationContext extends BaracusApplicationContext {
<build>
<finalName>${project.artifactId}</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>android-maven-plugin</artifactId>
<version>${android.plugin.version}</version>
<extensions>true</extensions>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>android-maven-plugin</artifactId>
<version>${android.plugin.version}</version>
<extensions>true</extensions>
<configuration>
<sdk>
<platform>${android.sdk.version}</platform>
</sdk>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
</plugin>
</pre>
<br />
So, basically, I chose the setting mostly equals to the one in the framework. Also, I switched to use the new ModelBase class, since the AbstractModelBase becomes unavailable in this release.
<br />
<br />
<b>Problem</b> : The android project did not compile any more. But it did with the old plugin!?! WTF?!!<br />
<br />
<pre class="code" name="text">[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /home/marcus/tutorial-fix/baracus-tutorial/src/main/java/net/mantucon/baracus/HelloAndroidActivity.java:[57,25] cannot find symbol
symbol: variable layout
location: class net.mantucon.baracus.R
[ERROR] /home/marcus/tutorial-fix/baracus-tutorial/src/main/java/net/mantucon/baracus/HelloAndroidActivity.java:[89,57] cannot find symbol
symbol: variable menu
location: class net.mantucon.baracus.R
[INFO] 2 errors
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.913 s
[INFO] Finished at: 2014-07-02T15:28:54+01:00
[INFO] Final Memory: 27M/336M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project baracus-tutorial: Compilation failure: Compilation failure:
[ERROR] /home/marcus/tutorial-fix/baracus-tutorial/src/main/java/net/mantucon/baracus/HelloAndroidActivity.java:[57,25] cannot find symbol
[ERROR] symbol: variable layout
[ERROR] location: class net.mantucon.baracus.R
[ERROR] /home/marcus/tutorial-fix/baracus-tutorial/src/main/java/net/mantucon/baracus/HelloAndroidActivity.java:[89,57] cannot find symbol
[ERROR] symbol: variable menu
[ERROR] location: class net.mantucon.baracus.R
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
Process finished with exit code 1
</pre>
<br />
After searching a little bit, I found out that the class R exists twice. I imported the BARACUS framework and it shares the package namespace with the tutorial.
<br />
<br />
<b>Reason</b>: The latest Android SDK and plugin do not allow the Mixup of R's within the same namespace any more.<br />
<br />
<b>Solution</b> : I did a refactoring, moving all tutorial classes to the baracustutorial namespace. This fixed the issue. Now the error disappeard. Thanks to IDEA Intellij, this was less than 10mins per project. Annoying was the fact, that every R-access had to be removed by Hand.<br />
<br />
<br />
<br />
<br />
<br />MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-75368072957070203652014-05-06T03:05:00.001-07:002015-03-20T00:53:39.771-07:00BARACUS from Scratch - Part 8 - Lifecycle support for your app with migr8
<a href="http://baracusframework.blogspot.de/p/baracus-from-scratch-table-of-contents.html"><b>Table of Contents</b></a><br />
<i><br /></i>
<i>In this tutorial I am going to show You, how to manage the database through the lifecycle of Your application.
Managing the persistence model and the underlying database structures is a quite important section in the lifecycle management of every application installed on a device not reacheable by the distributor. migr8 is the proper solution for reducing the pain of this management. It leverages the android and sqlite builtins to make database migration as easy as possible. </i><br />
<br />
Previous Tutorial : <a href="http://baracusframework.blogspot.de/2014/03/baracus-from-scratch-part-7-android.html">Database hot backup and hot recovery </a><br />
<br />
You can download the sourcecode from <a href="https://github.com/gorefest/baracus-framework/tree/Tutorial-Part-8">here</a><br />
<h2>
0 - Basics </h2>
<br />
Why is the management of database backends a problem on a mobile application? Because You do not have the guarantee that all clients running an upgrade are on the same version level. So there is a simple strategy necessary to avoid problems: It takes two information elements to make an update to an update and not to a problem : The current version of the database and the designated targert version. All steps transiting from the installed version to the target versions need to be known by Your application. This is a basic feature provided by Android/Sqlite.
All You need to do is to find a proper path for this transtions.<br />
<br />
And here migr8 enters the game.<br />
<br />
When You are dealing with persistence in Your application, the first bean you must register in Your ApplicationContext, is the OpenHelper:
<br />
<br />
<pre class="java" name="code">public class ApplicationContext extends BaracusApplicationContext {
static {
registerBeanClass(OpenHelper.class);
registerBeanClass(BankAccountDao.class);
registerBeanClass(CustomerDao.class);
...
</pre>
The OpenHelper is the core component of all DB Handling. So let us take a look onto it.<br />
<br />
<h2>
1 - The OpenHelper </h2>
<br />
In order to make use of Baracus persistence, Your application has to register a customized OpenHelper. When You take a look into the tutorial-application's OpenHelper You are going to find already three model changes registered inside of it :
<br />
<br />
<pre class="java" name="code">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);
}
}
</pre>
<br />
Now let us check the three relevant sections of the OpenHelper. The first information defined is the target version and the name of the database file. These are passed within the constructor to the super constructor in the bottom of the code.<br />
Any Upgrade to the model (which inflicts new MigrationStep implementations) results in increasing the TARGET_VERSION. Otherwise the steps are not hit and the model remains untouched.<br />
<br />
And in the middle, three migration steps are added. A migration step contains all relevant information to run a migration to a certain version.<br />
<br />
<h2>
2 - The migration step </h2>
<br />
So let us take a look on the ModelVersion100 Element :
<br />
<br />
<pre class="java" name="code">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;
}
}
</pre>
In this migration step the BankAccount table is defined using the column metadata of the persistence bean.
All You have to do is to define a correct sql creating the table.
You remember the OpenHelper? It got an instance of all Your MigrationSteps:
<br />
<br />
<pre class="java" name="code"> addMigrationStep(new ModelVersion100());
</pre>
<br />
<h2>
3 - Add a new migration step </h2>
<br />
In order to demostrate the ease of use of migr8, let's modify the BankAccount entity. At first, we add the field, the metadata and the gettersetters for a field named comment to the BankAccount entity:
<br />
<br />
<pre class="java" name="code">public class BankAccount extends AbstractModelBase {
public static final String TABLE_BANK_ACCOUNT = "bank_account";
...
public static final Field commentCol= new Field("comment", columnIndex++);
static {
...
fieldList.add(commentCol);
}
...
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
}
</pre>
<br />
Next, we need to extend the BankAccountDao's RowMapper in order to map the new field to the bean:
<br />
<br />
<pre class="java" name="code">public class BankAccountDao extends BaseDao<BankAccount> {
private final RowMapper<BankAccount> rowMapper = new RowMapper<BankAccount>() {
@Override
public BankAccount from(Cursor c) {
BankAccount result = new BankAccount();
...
result.setComment(c.getString(commentCol.fieldIndex));
return result;
}
@Override
public ContentValues getContentValues(BankAccount account) {
ContentValues result = new ContentValues();
...
if (account.getComment() != null) {
result.put(BankAccount.commentCol.fieldName, account.getComment());
}
return result;
}
};
}
</pre>
<br />
Finally, in order to make the stuff work, we must alter the database table in order to append these column to it. Therefore we will implement a MigrationStep:
<br />
<br />
<pre class="java" name="code">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;
}
}
</pre>
<br />
In order to finish Our masterpiece, we simply have to add it to the list of migrationsteps in the OpenHelper and increase the target version number:
<br />
<br />
<pre class="java" name="code">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());
}
...
</pre>
<br />
That's it.<br />
<br />
The next time You launch Your app the migration step will run and do the update job on Your data model.
<br />
<br />
Best practice hints :<b> Never ever</b> modify an existing migration step once after it has gone to production! You will probably cause disfunctional versions of Your app. Always add a new migration step to Your app.<br />
<br />
<h2>
Conclusion</h2>
<br />
<i>As You can see there is not much magic behind the migration step concept. But doing model modification in a stream of upgrades enables You to upgrade any version of the model of Your app without having any pain.</i><br />
<i><br /></i>
<i>Follow Up : <a href="http://baracusframework.blogspot.de/2014/10/tutorial-9-writing-custom-validators.html">Writing Custom Validators</a></i><br />
<br />
<br />
<br />MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-25239837215406922732014-04-03T03:31:00.000-07:002015-03-20T00:53:09.734-07:00BARACUS from Scratch - Part 7 - Android SQLite HotBackup and HotRecovery<a href="http://baracusframework.blogspot.de/p/baracus-from-scratch-table-of-contents.html"><b>Table of Contents</b></a><br />
<i><br /></i>
<i>In this tutorial I am going to show You, how to hot-backup the SQLite Database of Your Application. </i><br />
<i>If You rely on Baracus' migr8, even old Databases can be restored automatically and updated to the latest level. This offers You a flexible mechanism to deal backup and upgrade issues.</i><br />
<br />
Previous Tutorial : <a href="http://baracusframework.blogspot.de/2014/02/baracus-from-scratch-part-6-automatic.html">Automatic Form Validation</a><br />
<br />
You can download the sourcecode from <a href="https://github.com/gorefest/baracus-tutorial/tree/Tutorial-Part-7">here</a><br />
<br />
<i>Note : I found a pitfall; to log the last start/end date I added an onDestroy() listener to the configuration service in order to update the regarding table. This runs into an error, if the database is shutdown for restoring, so I simply caught the exception. Baracus has no mechanism atm which allows You to ask for the lifecycle state of the container. This issue will be solved soon. Current development version is 0.8-SNAPSHOT, so expect this issue to be solved somehow in 0.8+</i>.<br />
<div>
<br /></div>
Baracus relies on the effect, that Android keeps an open file handle to the database. This behaviour<br />
could be verified on all Samsung Devices I own (S3 mini, S4, Google Nexus 10), so if anybody faces a<br />
misbehaviour on a certain device, any email notification about this issue would be pretty welcome<br />
<br />
<h2>
Before all : Prerequisites </h2>
<br />
In order to perform android database backups You must add the WRITE_EXTERNAL_STORAGE privilege to the android manifest<br />
<br />
<pre class="xml" name="code"><manifest>
...
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>
</pre>
<br />
Once You have added this privileges, Your app is allowed to write to the device's external storage (phone : internal phone storage).<br />
<br />
Here we go!<br />
<br />
<h2>
Step 1 : Define a button in order to fire the backup procedure</h2>
Yes, a button. Simple and easy to implement, huh? We need it to start the backup ;-)<br />
<br />
<pre class="xml" name="code">android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Perform Backup"
android:id="@+id/btnPerformBackup" android:layout_alignRight="@+id/buttonBackup" android:layout_alignParentTop="true"
android:layout_marginTop="39dp"
android:onClick="onButtonBackupClicked"/>
</pre>
<br />
Now we have to wire that button<br />
<br />
<h2>
Step 2 : Call the built-in Backup function</h2>
Next step is to define the callback function in the HelloAndroidActivity :<br />
<br />
<pre class="java" name="code">public class HelloAndroidActivity extends Activity {
...
static String backupName;
...
public void onButtonBackupClicked(View view) {
DBBackup.BackupResult result = DBBackup.performDatabaseBackup();
if (result.isSuccessful()) {
showPopup(this, "Successfully backed up "+result.getSize()+" to file "+result.getBackupDbName());
backupName = result.getBackupDbName();
} else {
showPopup(this, "Backup failed :( Reason :"+result.getReason());
}
}
</pre>
<br />
That's it. Database has been backup. I have no clue yet, if the backup file itself is portable to some other device type, but at the moment<br />
I doubt it, relying on the misportability of native structured storage between two platforms. If I get some other device type I'll give a try. The name of the backup file is held in the static variable backupName. At the moment Baracus defines it own name built from timestamp, databasename and a .tac ending (truely assorted content).<br />
<br />
A Backup is completely useless if You cannot perform any recovery with it, so here we go...<br />
<h2>
Step 3 : Implement ... another button</h2>
This button adds a new Customer to the table named Jane Doe. We need it, to show the impatient nonbelievers, that the current database truely has been modified.<br />
<br />
<pre class="xml" name="code"><Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Modify Data"
android:id="@+id/btnPerformDataModification" android:layout_alignRight="@+id/buttonModifiy" android:layout_alignParentTop="true"
android:layout_marginTop="39dp"
android:onClick="onButtonModifyClicked"/>
</pre>
<br />
and here comes the callback :<br />
<br />
<pre class="java" name="code">public class HelloAndroidActivity extends Activity {
...
public void onButtonModifyClicked(View view) {
Customer janeDoe = new Customer();
janeDoe.setFirstName("Jane");
janeDoe.setLastName("Doe");
customerDao.save(janeDoe);
showPopup(this, "Added Jane Doe");
}
</pre>
<br />
So when this callback is fired, a customer entity is created and written into the database. Basically this has got nothing to do with database backup, but it is better to see the the database modification within the customer subdialog.<br />
<h2>
Step 4 : Implement the recovery button</h2>
OK, now let's implement the recovery button. The impatient already checked if the data really has been modified by the button, right?<br />
<br />
<pre class="xml" name="code"><Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Backup Database"
android:id="@+id/btnRestorDatabase" android:layout_alignRight="@+id/buttonRestore" android:layout_alignParentTop="true"
android:layout_marginTop="39dp"
android:onClick="onBtnRestoreClicked"/>
</pre>
<br />
<h2>
Step 5 : Recovery</h2>
Recovery is a little bit more complicated because it requires the application context to shut down.<br />
So if You did not break any rules like static dangling of objects and so on You should be able to shutdown, restore and relaunch the application without noticing it - if you are messing around with persistence calls in the destroy() lifecycle like me, You have to clean up Your backyard first. The restore util takes care of the shutdown, restore and relaunch process.<br />
<br />
This looks like this :<br />
<br />
<pre class="java" name="code"> public void onBtnRestoreClicked(View view) {
DBBackup.BackupResult result = DBBackup.restore(backupName);
if (result.isSuccessful()) {
showPopup(this, "Restore completed successfully.");
} else {
showPopup(this, "Restore failed due to reason :" + result.getReason());
}
}
</pre>
<br />
<br />
Normally, You now can verify, that Jane Doe is not in Our database any more. Simply verify it by checking the table.<br />
<br />
<h2>
Conclusion</h2>
<i>As You can see, Hotbackupping Your database is not complicated at all. At the moment I am quite confident that backups can be performed on each android device, but with the emerging device fragmentation problem this is not absulutely clear.</i><br />
<i><br /></i>Upcoming tutorial : <a href="http://baracusframework.blogspot.de/2014/05/baracus-from-scratch-part-8-lifecycle.html">Managing the database with migr8</a><br />
<br />MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-48987399747163308562014-02-10T10:53:00.001-08:002015-03-20T00:50:13.961-07:00BARACUS from Scratch - Part 6 - Automatic Form Validation (Chapter Two of Two)<b><a href="http://baracusframework.blogspot.de/p/baracus-from-scratch-table-of-contents.html">Table of Contents</a></b><br />
<br />
<i>In this tutorial, we are going to apply the newly learned knowledge and start creating a form including validation. This tutorial is based on the part one of the automatic form validation chapter. So You should check out that, first.</i><br />
<i><br /></i>
<br />
<i>Previous Tutorial : <a href="http://baracusframework.blogspot.com/2014/01/baracus-from-scratch-part-6-automatic.html">Automatic Form Validation (Chapter one of two)</a></i><br />
<br />
<a href="https://github.com/gorefest/baracus-tutorial/tree/Tutorial-Part-6">Download the sourcecode for this tutorial from github</a><br />
<h2>
Step 1 : We need a form!</h2>
<div>
To validate a form, we first need a form. Therefore, we are going to add a customer_editor.xml to the res/layout files. For the beginning, we only place a horizontal layout and the labels and the save button to the xml :</div>
<div>
<br /></div>
<div>
<br /></div>
<pre class="xml" name="code"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/customerFirstName"
android:id="@+id/customerFirstNameLabel" android:layout_gravity="center"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/customerLastName"
android:id="@+id/customerNameLabel" android:layout_gravity="center"/>
<Button
android:id="@+id/btnSave"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/saveCustomer"
android:onClick="btnSaveClicked"
/>
</LinearLayout>
</pre>
<br />
Do not forget to add the labels to the strings.xml :<br />
<br />
<pre class="xml" name="code">...
<string name="customerFirstName">First Name</string>
<string name="customerLastName">Last Name</string>
<string name="saveCustomer">save customer</string>
...
</pre>
<br />
So far, so good. Now let's add the Text Edit Fields.<br />
<br />
<h2>
Step 2 : Adding the fields</h2>
In order to validate fields, we learned, that we need to add vaildateable fields. These are brought in by the framework. Please notice, that the app:validatedBy denotes the named constraint which is applied to the field:<br />
<br />
<pre class="xml" name="code"><net.mantucon.baracus.ui.ConstrainedEditText android:id="@+id/txtCustomerFirstName"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:validatedBy="stringNotEmpty"
/>
</pre>
<br />
and
<br />
<pre class="xml" name="code"><net.mantucon.baracus.ui.ConstrainedEditText android:id="@+id/txtCustomerLastName"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:validatedBy="stringNotEmpty" />
</pre>
<br />
The stringNotEmpty-Constraint is a framework built-in. Later, we are going to build Our own custom validator.<br />
<h2>
Step 3 : Form Class</h2>
After we have added the fields, we need to create the java class for this form. I chose an Activity for this, but You also can use a Fragment. In order to have luxury goods like automatic form validation, dependency injection and so on, BARACUS introduces the <a href="http://www.baracus.org/apidocs/0.7/net/mantucon/baracus/context/ManagedFragment.html">ManagedFragment</a> and the <a href="http://www.baracus.org/apidocs/0.7/net/mantucon/baracus/context/ManagedActivity.html">ManagedActivity</a> class. Notice, ManagedFragment also is needed, if Your application supports device rotation (internally, the device calls the constructor of the Fragment class and this causes all injected by BARACUS to be nullified; this problem is solved by the ManagedFragment).<br />
<br />
There is the form class :<br />
<br />
<pre class="java" name="code">package net.mantucon.baracus;
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.widget.TextView;
import net.mantucon.baracus.annotations.Bean;
import net.mantucon.baracus.application.ApplicationContext;
import net.mantucon.baracus.context.ManagedActivity;
import net.mantucon.baracus.dao.CustomerDao;
import net.mantucon.baracus.model.Customer;
import net.mantucon.baracus.validation.ValidationFactory;
/**
* Created by marcus on 03.02.14.
*/
public class CustomerEditorActivity extends ManagedActivity {
TextView lastName; // sic.
TextView firstName;
Customer customer;
@Bean
CustomerDao customerDao;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.customer_editor);
enableFocusChangeBasedValidation();
lastName = (TextView) findViewById(R.id.txtCustomerLastName);
firstName = (TextView) findViewById(R.id.txtCustomerFirstName);
// If we edit an existing customer, load the customer data
Bundle extras = getIntent().getExtras();
if (extras != null) {
Long customerId = extras.getLong("customerId");
customer = customerDao.getById(customerId);
lastName.setText(customer.getLastName());
firstName.setText(customer.getFirstName());
} else {
customer = new Customer();
}
}
public void btnSaveClicked(View v) {
if (validate()) { // re-validate, is it ok?
customer.setFirstName(firstName.getText().toString()); // ok : take data, write it back into DB.
customer.setLastName(lastName.getText().toString());
customerDao.save(customer);
dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK)); // go back to caller
finish();
} else {
Toast.makeText(this, R.string.insufficientData, Toast.LENGTH_LONG).show();
}
}
}
</pre>
<br />
Pretty easy, huh? So this is all we need. If You violate one of the declared constraint an automatic notification happens to the form - the constraint violating form components become highlighted and a toast is displayed.
<br />
<br />
<h2>
Step 4 : Wiring the dialogs</h2>
<br />
OK, now let's wire this activity to the caller Activity. I want a Customer -> BankAccount expandable List, wiring the long touch on the customer as the open action for the customer editor activity.<br />
<br />
To dangle the stuff, at first we modify the main XML. We add the ExpandableListView to Our main :<br />
<br />
<br />
<pre class="xml" name="code">...
<expandablelistview android:groupindicator="@null" android:id="@+id/expandableListView" android:layout_height="match_parent" android:layout_width="match_parent"/>
...
</pre>
<br />
Next, we need the layout description for the group and the child item.<br />
entry_list_group_item.xml :<br />
<pre class="xml" name="code"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="55dip"
android:orientation="vertical" >
<TextView
android:id="@+id/tvGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</pre>
<br />
and entries_list_child_item<br />
<pre class="xml" name="code"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="55dip"
android:orientation="horizontal" android:selectAllOnFocus="true" >
<TextView
android:id="@+id/entryName"
android:layout_width="150dip"
android:layout_height="wrap_content"
android:textSize="14dip"/>
<TextView
android:id="@+id/entryAccount"
android:layout_width="175dip"
android:layout_height="wrap_content"
android:textSize="14dip"/>
</LinearLayout>
</pre>
<br />
To manage an ExpandableList View, we are going to need a suitable Adapter. This adapter is derived from BaseExpandableListAdapter :<br />
<pre class="java" name="code">package net.mantucon.baracus;
import android.app.Activity;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;
import net.mantucon.baracus.application.ApplicationContext;
import net.mantucon.baracus.model.BankAccount;
import net.mantucon.baracus.model.Customer;
import net.mantucon.baracus.signalling.DataChangeAwareComponent;
import java.util.ArrayList;
import java.util.List;
/**
* List Adapter Class for displaying accounts
*/
public class AccountExpandListAdapter extends BaseExpandableListAdapter {
private Activity context; // we need this one to inflate the detail rows
private List<Customer> groups; // customer master rows
public AccountExpandListAdapter(Activity context, List<Customer> groups) {
this.context = context;
this.groups = groups;
}
/**
* @param groupPosition - the customer row index
* @param childPosition - the account row index
* @return child object from group x, child y
*/
public Object getChild(int groupPosition, int childPosition) {
List<BankAccount> entries = groups.get(groupPosition).getAccounts();
ArrayList<BankAccount> chList = new ArrayList<BankAccount>(entries);
return chList.get(childPosition);
}
/**
* @param groupPosition
* @param childPosition
* @return child Id which is equal to our position
*/
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
/**
* create the child view for a child on a certain position
*
* @param groupPosition - the customer row index
* @param childPosition - the account row index
* @param isLastChild
* @param view - the view (the child view, can be null)
* @param parent - the parent view (the customer)
* @return the view
*/
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View view,
ViewGroup parent) {
final BankAccount child = (BankAccount) getChild(groupPosition, childPosition);
if (view == null) {
LayoutInflater infalInflater = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
view = infalInflater.inflate(R.layout.entries_list_child_item, null);
}
final TextView entryName = (TextView) view.findViewById(R.id.entryName);
entryName.setText(child.getBankName().toString());
final TextView entryValue = (TextView) view.findViewById(R.id.entryAccount);
entryValue.setText(child.getIban());
return view;
}
public int getChildrenCount(int groupPosition) {
ArrayList<BankAccount> chList = new ArrayList<BankAccount>(groups.get(groupPosition).getAccounts());
return chList.size();
}
public Object getGroup(int groupPosition) {
return groups.get(groupPosition);
}
public int getGroupCount() {
return groups.size();
}
public long getGroupId(int groupPosition) {
return groupPosition;
}
/**
* creat the customer view
* @param groupPosition
* @param isLastChild
* @param view
* @param parent
* @return the customer view
*/
public View getGroupView(int groupPosition, boolean isLastChild, View view,
ViewGroup parent) {
Customer group = (Customer) getGroup(groupPosition);
if (view == null) {
LayoutInflater inf = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
view = inf.inflate(R.layout.entries_list_group_item, null);
}
TextView tv = (TextView) view.findViewById(R.id.tvGroup);
tv.setText(group.getLastName()+", "+group.getFirstName());
return view;
}
public boolean hasStableIds() {
return true;
}
public boolean isChildSelectable(int arg0, int arg1) {
return true;
}
}
</pre>
<br />
Finally, we dangle the stuff into the HelloAndroidActivity. So the onCreate() method is extended like this :<br />
<br />
<br />
<pre class="java" name="code">...
expandableListView = (ExpandableListView) findViewById(R.id.expandableListView);
fillTable();
...
/**
* fill the data trable
*/
private void fillTable() {
final List<Customer> customers = customerDao.loadAll();
AccountExpandListAdapter adapter = new AccountExpandListAdapter(this, new ArrayList<Customer>(customers));
expandableListView.setAdapter(adapter);
expandableListView.setLongClickable(true);
expandableListView.setClickable(true);
// Handle the long click; hold the customer long to open the CustomerEditor Activity
expandableListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent = new Intent(HelloAndroidActivity.this, CustomerEditorActivity.class);
ArrayList<Customer> chList = new ArrayList<Customer>(customers);
Customer c = chList.get(position);
intent.putExtra("customerId", c.getId()); // pass the customer ID as parameter to the activity
HelloAndroidActivity.this.startActivity(intent);
return false;
}
});
}
...
</pre>
Notice, there is currently no refresh done automatically. Therefore, we modify the onClick method by simply adding the fillTable() call :<br />
<br />
<br />
<pre class="java" name="code"> public void onButtonTestClicked(View v) {
...
// refill the data table
fillTable();
}
</pre>
<br />
Ok, to add customers, we will have to add a "new" button at last. Therefore we extend the main_layout.xml and insert a button (Do not forget to add a label in the strings.xml!)
<br />
<pre class="xml" name="code"><Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New"
android:id="@+id/btnNew" android:layout_alignRight="@+id/button" android:layout_alignParentTop="true"
android:layout_marginTop="39dp"
android:onClick="onButtonNewClicked"/>
</pre>
<br />
To finish the dialog, simply add the onClick event handler to the activity : <br />
<pre class="java" name="code">public void onButtonNewClicked(View view) {
Intent intent = new Intent(HelloAndroidActivity.this, CustomerEditorActivity.class);
HelloAndroidActivity.this.startActivity(intent);
}
</pre>
<br />
<h2>
Conclusion</h2>
<div>
<i>Using the BARACUS framework built-in form validation technique, message routing and error handling becomes really easy. You have learned how to wire validators to UI components and how to perform pre-saving validation. Because we used the ManagedActivity's focusChangeBasedValidation, the components are validated every time we change the widget.</i></div>
<div>
<i><br /></i></div>
<div>
<i>Follow up : <a href="http://baracusframework.blogspot.de/2014/03/baracus-from-scratch-part-7-android.html">Database Hot backup and recovery</a></i></div>
<div>
-</div>
MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-61928944675824693792014-01-22T06:54:00.000-08:002015-03-20T00:49:55.864-07:00BARACUS from Scratch - Part 6 - Automatic Form Validation (Chapter one of Two)<a href="http://baracusframework.blogspot.de/p/baracus-from-scratch-table-of-contents.html"><b>Table of Contents</b></a><br />
<br />
Previous Tutorial : <a href="http://baracusframework.blogspot.de/2013/10/baracus-from-scratch-part-5-advanced.html">Advanced Persistence Mapping</a><br />
<i><br /></i>
<i>In this tutorial I am going to explain You the benefits of automated form validation. This feature is essiential to me to have valid data in a form in order to edit or add database content in my applications. Basically, I adopted the concept of form validation from jsf. You can register named validators (a variety of validators already is defined) and declare them to be applied on Your component. Because all data is held in TextViews, there currently only exists a ConstrainedEditText component but it is easy to extend this system.</i><br />
<i><br /></i>
<br />
<h2>
The Basics I - Validators and the ValidationFactory</h2>
<div>
Imagine You have a pool of validators registered in Your application. Each validator is capable to determine that a information passed to it is valid or not. Additionally, each validator has a unique name which makes it identifiable within the container. A validator is passed to the ValidationFactory using either a name or not; if You don't supply a name, the name will be the class simple name with a small capital first character (FooValidator -> fooValidator). Because You can pass fully instantiated class instances to the registry, You also use Beans (and all the luxuries of a managed bean) for validation tasks.</div>
<div>
<br /></div>
<div>
The ValidatorFactory has a second task. It is able to perform an explicit validation on a view. It does this by recursively iterating the form and looking for instances of the <span style="background-color: white; color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; line-height: 18px; white-space: pre;">ConstrainedView </span>interface. When it finds a constrained view implementation, it takes the validators named in the <span style="background-color: white; color: teal; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; line-height: 18px; white-space: pre;">app:validatedBy </span>field and tries to validate the information.</div>
<div>
<br /></div>
<div>
Anything implementing the <span style="background-color: white; color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; line-height: 18px; white-space: pre;">ConstrainedView </span>interface can be used to validate a form.</div>
<div>
<br /></div>
<div>
There are already a set of predefined validators ready-to-use :</div>
<div>
<ul>
<li>DateFromNow</li>
<li>NumberMustBeGreaterThanZero</li>
<li>StringIsNumericDouble</li>
<li>StringIsNumericInteger</li>
<li>StringNotEmpty</li>
</ul>
There will be more validators in future soon. These validators perform basic (atomic) validations of a single attribute and are ready to use.<br />
<br />
Every validator needs to know about the error message to route. This is a text information placed in the strings.xml. The built in validators have only english texts at the moment (any extension help welcome :)).</div>
<div>
<br /></div>
<h2>
The Basics II - Declaring the use of a validator</h2>
<div>
Now this is easy. The <span style="background-color: white; color: navy; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; line-height: 18px; white-space: pre;">org.baracus.ui.ConstrainedEditText </span>commponent carries an attribute named <span class="na" style="background-color: white; box-sizing: border-box; color: teal; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; line-height: 18px; white-space: pre;">app:validatedBy</span>. This attribute carries a comma seperated list of all validator names to be applied. The order of the validators in the string is the order the validation is done. So in order to check the component not to have empty text, the xml declaration looks like this : </div>
<div>
<br /></div>
<pre class="xml" name="code"><org.baracus.ui.ConstrainedEditText android:id="@+id/txtFirstName"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:validatedBy="stringNotEmpty"
/>
</pre>
<br />
That's it. Because the validation is runtime-evaluated, You have to manually check that validation works at least once; the validation factory will produce an exception if an unidentifiable validator is used. The validators are seperated by using a comma.<br />
<br />
<h2>
The Basics III - choosing the validation strategy</h2>
<div>
There are three strategies for automated form validation: validating explicitly (routing the errors manually placing your validation code into the controller), implicitely using the ApplicationContext's or, third way, focusChangedBasedValidation automatic validation using a ManagedFragment or a ManagedActivity.</div>
<div>
<br /></div>
<div>
The Application context offers a number of methods for validation (depending on the strategy You choose).</div>
<div>
<br /></div>
<h3>
Explicit, controller based validation without validator beans</h3>
<div>
The first solution enters the game, if you want to validate e.g. on button click having complex validation logics in Your controller. This strategy does not need any controller bean; your controller does all the validation logics<br />
<br />
Then You can define the explicit validation in Your controller:</div>
<div>
<br /></div>
<pre class="java" name="code">public void btnSaveClicked(View v){
// clear all error notifications, we are going to validate here
ApplicationContext.resetErrors(underlyingView);
// explicit form validation
if (accountNumber.getText() == null || accountNumber.getText().toString().trim().length() ==0) {
ApplicationContext.addErrorToView(underlyingView, R.id.txtAccountNumber, R.string.notNullAccountNumber, ErrorSeverity.ERROR);
// ... here could be more validations
// ... and here we route them to the form :
ApplicationContext.applyErrorsOnView(underlyingView);
} else {
currentAccount.setAccountNumber(accountNumber.getText().toString());
currentAccount.setAccountName(accoutName.getText().toString());
accountDao.save(currentAccount); // This will fire the DataChanged Event on the MainActivity
onBackPressed();
}
}
</pre>
<br />
As You can see, every errors are routed explicitly to form fields. Finally, You can order the context to apply the errors to the fields.<br />
<br />
<h3>
Implicit, controller based validation</h3>
<div>
Instead of relying on controller logics, you can attach - like shown in basics I - named validators to the component and let the ApplicationContext do the controller logics.</div>
<br />
<pre class="java" name="code">public void btnSaveClicked(View v){
// clear all error notifications, we are going to validate here
ApplicationContext.resetErrors(underlyingView);
/* implicit form validation
* The validation wiring is done on the validatedBy-Property inside of
* the xml-declaration of the components
* The routing is determined by the use of special error views (case 1)
* or a mappeable component (TextViews are processed by @see TextEditErrorHandlers) */
ApplicationContext.validateView(underlyingView);
if (!ApplicationContext.viewHasErrors(underlyingView)) {
currentCustomer.setFirstName(firstName.getText().toString());
currentCustomer.setLastName(lastName.getText().toString());
customerDao.save(currentCustomer);
onBackPressed();
}
}
</pre>
<pre class="java" name="code"></pre>
<pre class="java" name="code"></pre>
So all the dirty work of routing error messages is done by calling validateView for the form view. Before doing this, all existing errors should be removed. The validation itself is done via the above mentioned validators. These itchy little helpers get applied to the form and are used to decide, whether an error message has to be routed to the component or not.<br />
<br />
<h3>
Fully automatic validation</h3>
<div>
You also can use fully automatic validation. Therefore, Your Activity must be inheriting the baracus' ManagedActivity - or if fragments are used - the ManagedFragment. Both components offer the <span style="background-color: white; color: #990000; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; font-weight: bold; line-height: 18px; white-space: pre;">enableFocusChangeBasedValidation </span>function in order to manage a validation, when a ui component focus changes.</div>
<br />
This will enable You to have automatic validation and error routing to all your constrained components whenever a focus changes (this also includes the removal of errors from corrected input fields).<br />
<br />
Any further - more or less complex - validation must be implemented by hand.<br />
<br />
<h2>
The Basics IV - Message routing</h2>
<div>
If you simply attach the validator to Your input field, baracus will make use of the standard android error handling function. But there is another trick You might use for handling complex error output. Therefore You can define a custom error view, baracus will use instead of the standard error handling. A custom error handler is defined like this :</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<pre style="box-sizing: border-box; color: #333333; font-size: 12px; line-height: 18px;"><div class="line" id="LC21" style="box-sizing: border-box; padding-left: 10px;">
<span style="font-family: Courier New, Courier, monospace;"> <span class="nt" style="box-sizing: border-box; color: navy;"><org.baracus.ui.ErrorView</span> <span class="na" style="box-sizing: border-box; color: teal;">android:id=</span><span class="s" style="box-sizing: border-box; color: #dd1144;">"@+id/customerFirstMsg"</span></span></div>
<span style="font-family: Courier New, Courier, monospace;">
</span><div class="line" id="LC22" style="box-sizing: border-box; padding-left: 10px;">
<span style="font-family: Courier New, Courier, monospace;"> <span class="na" style="box-sizing: border-box; color: teal;">app:displayMessageFor=</span><span class="s" style="box-sizing: border-box; color: #dd1144;">"@id/txtCustomerFirstName"</span></span></div>
<span style="font-family: Courier New, Courier, monospace;">
</span><div class="line" id="LC23" style="box-sizing: border-box; padding-left: 10px;">
<span style="font-family: Courier New, Courier, monospace;"> <span class="na" style="box-sizing: border-box; color: teal;">app:highlightTarget=</span><span class="s" style="box-sizing: border-box; color: #dd1144;">"true"</span></span></div>
<span style="font-family: Courier New, Courier, monospace;">
</span><div class="line" id="LC24" style="box-sizing: border-box; padding-left: 10px;">
<span style="font-family: Courier New, Courier, monospace;"> <span class="na" style="box-sizing: border-box; color: teal;">android:layout_width=</span><span class="s" style="box-sizing: border-box; color: #dd1144;">"wrap_content"</span></span></div>
<span style="font-family: Courier New, Courier, monospace;">
</span><div class="line" id="LC25" style="box-sizing: border-box; padding-left: 10px;">
<span style="font-family: Courier New, Courier, monospace;"> <span class="na" style="box-sizing: border-box; color: teal;">android:layout_height=</span><span class="s" style="box-sizing: border-box; color: #dd1144;">"wrap_content"</span></span></div>
<span style="font-family: Courier New, Courier, monospace;">
</span><div class="line" id="LC26" style="box-sizing: border-box; padding-left: 10px;">
<span style="font-family: Courier New, Courier, monospace;"> <span class="na" style="box-sizing: border-box; color: teal;">android:layout_marginLeft=</span><span class="s" style="box-sizing: border-box; color: #dd1144;">"10dp"</span></span></div>
<span style="font-family: Courier New, Courier, monospace;">
</span><div class="line" id="LC27" style="box-sizing: border-box; padding-left: 10px;">
<span style="font-family: Courier New, Courier, monospace;"> <span class="na" style="box-sizing: border-box; color: teal;">android:editable=</span><span class="s" style="box-sizing: border-box; color: #dd1144;">"true"</span></span></div>
<span style="font-family: Courier New, Courier, monospace;">
</span><div class="line" id="LC28" style="box-sizing: border-box; padding-left: 10px;">
<span style="font-family: Courier New, Courier, monospace;"> <span class="na" style="box-sizing: border-box; color: teal;">android:textColor=</span><span class="s" style="box-sizing: border-box; color: #dd1144;">"#FF0000"</span></span></div>
<span style="font-family: Courier New, Courier, monospace;">
</span><div class="line" id="LC29" style="box-sizing: border-box; padding-left: 10px;">
<span style="font-family: Courier New, Courier, monospace;"> <span class="na" style="box-sizing: border-box; color: teal;">android:lines=</span><span class="s" style="box-sizing: border-box; color: #dd1144;">"1"</span></span></div>
<span style="font-family: Courier New, Courier, monospace;">
</span><div class="line" id="LC30" style="box-sizing: border-box; padding-left: 10px;">
<span style="font-family: Courier New, Courier, monospace;"> <span class="na" style="box-sizing: border-box; color: teal;">android:text=</span><span class="s" style="box-sizing: border-box; color: #dd1144;">""</span><span class="nt" style="box-sizing: border-box; color: navy;">/></span></span></div>
<span style="font-family: Courier New, Courier, monospace;">
</span><span style="font-family: Consolas, Liberation Mono, Courier, monospace;">
</span></pre>
</div>
<br />
with the displayMessageFor-information You tell baracus, that this specific fields is responsible for the error handling of @id/txtCustomerFirstName.<br />
<br />
with the highlightTarget-informatrion You tell baracus to highlight the error source field when a message is displayed.<br />
<br />
This technique is useful e.g. if You want to display an error image instead of textual information.<br />
<br />
<h2>
Conclusion</h2>
With the three mechanisms of error handling, field validation and error routing, You get a powerful tools to make form-based android development more comfortable - no more writing of in-controller boilerplate validation code. You can make benefit of the use of named validators and the possibility to reuse validation code.<br />
<br />
<i>Best practice : I found for myself making ManagedBeans out of all my fragments has proven to be a good choice to make development more comfortable. You get the luxury of dependency injection inside of Your fragments, and You can make use of all container functions baracus offers. Therefore, You MUST used ManagedFragments (It is an android malice to make internal re-instantiation of fragments on device rotation); otherwise all injected fields are nulled!</i><br />
<i><br /></i>COMING SOON : In the next tutorial I am going to show You, how to implement these validation techniques in the demo app.MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-16312189385340002272013-12-31T05:53:00.000-08:002013-12-31T05:53:00.763-08:00BARACUS stable version 0.7 releasedToday I am glad to announce that BARACUS framework version 0.7 has been released.<br />
<br />
It can be included using the coordinates<br />
<br />
<pre class="xml" name="code"><dependency>
<groupid>org.baracus</groupid>
<artifactid>baracus-framework</artifactid>
<version>0.7</version>
<type>apklib</type>
</dependency>
</pre>
<br />
<br />
Also, I upgraded the baracus-demoapp to a newer android plugin version plus the new Baracus version. It can be downloaded from <a href="https://github.com/gorefest">my github repository</a>. It contains the demonstration code for automatic form validation (a new tutorial covering this topic has not been written yet).<br />
<br />
The branch with the sourcecode of this stable release <a href="https://github.com/gorefest/baracus-framework/tree/0.7-release-version">can be found here</a><br />
<br />
Due to the nature of this date, this will be the last release for this year ;-)<br />
<br />
Regards,<br />
mntMNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-90037127371008532752013-12-17T22:53:00.001-08:002013-12-17T22:53:13.714-08:00BARACUS now available via Maven Cetral RepositoryToday Baracus Framework now officially is accessible through Maven Cetral Repository.
The first available version is 0.7-Beta1, so if You use the coordinates
<br />
<pre class="xml" name="code">
<dependency>
<groupid>org.baracus</groupid>
<artifactid>baracus-framework</artifactid>
<version>0.7-Beta1</version>
<type>apklib</type>
</dependency>
</pre>
You now can use BARACUS without the MANTUCON repository.
The 0.7 Beta already includes - besides a number of bugfixes - automated / declarative form validation (a tutorial to this topic is going to follow until end of January).
Regards,
mntMNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-43512839578410482992013-10-28T11:35:00.000-07:002015-03-20T00:48:23.675-07:00BARACUS from Scratch - Part 5 - Advanced Persistence Mapping<a href="http://baracusframework.blogspot.de/p/baracus-from-scratch-table-of-contents.html"><b>Table of Contents</b></a><br />
<i><br /></i>
<i>Previous Tutorial : <a href="http://baracusframework.blogspot.de/2013/10/baracus-from-scratch-part-4-ioc-and-di.html">Inversion of Control & Dependency Injection</a></i><br />
<i><br /></i>
<i>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.</i><br />
<i><br /></i>
<i>This tutorial is based on the tutorial part 3, so if You have not read it, start from there.</i><br />
<i><br /></i>
<br />
<i><a href="https://github.com/gorefest/baracus-tutorial/tree/Tutorial-Part-5">Download the source code to this tutorial from Github</a></i><br />
<h2>
Step 1 : The basics</h2>
<div>
<br /></div>
<div>
We have modelled the BankAccount in the prior tutorial and fitted it with a <a href="http://en.wikipedia.org/wiki/Data_Access_Object">DAO </a>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 <a href="http://en.wikipedia.org/wiki/Lazy_loading">lazy loading</a> or eager loading in the place where the row data is mapped.</div>
<div>
<br /></div>
<div>
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.</div>
<div>
<br />
The proceeding is following the four steps mentioned in the prior tutorial (entity, dao+rowmapper, migr8 mode, bean wiring)<br />
<br />
<h3>
ONE - The entity</h3>
</div>
<div>
<br /></div>
<div>
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 :<br />
<br /></div>
<pre class="java" name="code">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;
}
}
</pre>
<br />
<br />
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<br />
<br />
<h3>
TWO - The DAO</h3>
<div>
<br /></div>
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 :<br />
<br />
<pre class="java" name="code">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;
}
}
</pre>
<br />
<br />
You can mostly copy the BankAccountDao and modify it properly using the Customer entity.<br />
<br />
<h3>
THREE - The migr8 model</h3>
<div>
<br /></div>
<div>
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:</div>
<br />
<pre class="java" name="code">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;
}
}
</pre>
<br />
As You can see, we make use of the column metadata to create the CREATE TABLE-statement.<br />
<br />
<h3>
FOUR - Wire the beans</h3>
<div>
<br /></div>
<div>
Now we wire the beans properly, this means a) registering the OpenHelper implementation and b) registering the DAO in the app initializer.</div>
<div>
<br /></div>
<pre class="java" name="code">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);
}
}
</pre>
<br />
<br />
As you can see, we increase the TARGET_VERSION and we add our MigrationStep. Finally, we register the DAO in the ApplicationContext :<br />
<br />
<pre class="java" name="code">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);
}
}
</pre>
<br />
<h2>
Step2 : Many-To-One relation</h2>
<br />
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.<br />
<br />
So basically, at first we create a field in the BankAccount using the Reference<Customer> type and create the designated database column :<br />
<br />
<br />
<pre class="java" name="code">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>(customer);
}
}
</customer></pre>
<br />
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).<br />
<br />
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 :<br />
<br />
<pre class="java" name="code">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;
}
}
</pre>
<br />
<br />
This migration step needs to be registered to the OpenHelper implementation, whose TARGET_VERSION is increased to 102 :
<br />
<pre class="java" name="code">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);
}
}
</pre>
<br />
<br />
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 :
<br />
<pre class="java" name="code">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;
}
};
// ...
}
</pre>
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.
<br />
<br />
<i>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.</i><br />
<i><br /></i>
<br />
<h2>
Step 3 : One-To-Many relation</h2>
<br />
Now lets proceed adding the (more trivial case) of the OneToMany relationship (Customer -- *BankAccount)<br />
<br />
Therefore we add the Collection<BankAccount> plus getter/setter to the class :<br />
<br />
<pre class="java" name="code">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;
}
}
</pre>
<br />
In order to make the stuff beeing filled with life, we have to write a DAO function retrieving the BankAccount list for each customer :<br />
<br />
<br />
<pre class="java" name="code">package org.baracus.dao;</pre>
<pre class="java" name="code">// ...
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;
}
</pre>
<br />
<i>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.</i><br />
<br />
Finally, we extend the Customer's RowMapper implementation in order to get the relation resolved :<br />
<br />
<br />
<pre class="java" name="code">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;
}
// ...
};
// ...
}
</pre>
<br />
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.<br />
<br />
Now that we have created a bidirectional relationship, let's try to use it!<br />
<br />
<h2>
Finish - tryout the relationship</h2>
In order to demonstrate the lazy stuff inside of our model, we are going to add some code to the MainActivity.<br />
<br />
<br />
<pre class="java" name="code">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());
}
}
}
</pre>
<br />
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!<br />
<br />
<i>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. </i><br />
<i><br /></i>
<i><br /></i>
Follow Up : <a href="http://baracusframework.blogspot.de/2014/01/baracus-from-scratch-part-6-automatic.html">Baracus approach for automatic form validation</a><br />
<i><br /></i>MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-66277281228169734282013-10-23T23:56:00.001-07:002015-03-18T12:08:42.288-07:00BARACUS from Scratch - Part 4 - IOC and DI<a href="http://baracusframework.blogspot.de/p/baracus-from-scratch-table-of-contents.html"><b>Table of Contents</b></a><br />
<br />
Previous Tutorial : <a href="http://baracusframework.blogspot.de/2013/10/baracus-from-scratch-part-3-persistence.html">Basic Persistence Mapping</a><br />
<i><br /></i>
<i>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.</i><br />
<br />
<h3>
<a href="https://github.com/gorefest/baracus-tutorial/tree/Tutorial-Part-4">Download the sourcecode of this tutorial from Github</a> </h3>
<h2>
Step 1 - Dependency Injection <i></i></h2>
<a href="http://en.wikipedia.org/wiki/Dependency_injection">Dependency Injection</a> (or shortened DI) is the most common and most well known aspect of IOC. IOC itself stands for <a href="http://en.wikipedia.org/wiki/Inversion_of_Control">Inversion of Control</a> and is basically summarizeable as the <a href="http://en.wikipedia.org/wiki/Hollywood_principle">"Hollywood principle"</a> in the Java bean world. The hollywood priciple says "Don't call us, we'll call You".<br />
<br />
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.<br />
<br />
<h3>
Beans are instantiated by the ApplicationContext</h3>
<br />
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.<br />
<br />
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 :<br />
<br />
<pre class="java" name="code">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;
}
</pre>
<br />
<br />
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.<br />
<br />
<div style="text-align: center;">
<b>This annotation is only for documentation purpose! The injection is done on the type of the bean class!</b> </div>
<br />
After creating the service bean, we need to register it in the ApplicationContext:<br />
<br />
<pre class="java" name="code">package org.baracus.application;
// ....
public class ApplicationContext extends BaracusApplicationContext {
static {
// ....
registerBeanClass(ConfigurationService.class);
}
}
</pre>
<br />
That's it. Finally let us place some sensibe function to demonstrate the encapsulation of a configuration parameter into the bean:<br />
<br />
<br />
<pre class="java" name="code">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);
}
}
</pre>
<br />
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.<br />
<h3>
DI : Conclusion</h3>
Baracus offers You a very simple - but lightweight - type based dependency injection, which has got certain limitations :<br />
<br />
<ol>
<li>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.</li>
<li>No polymorphisms, You cannot register a bean implementation twice!</li>
<li>Default constructors; Your bean must a) provice a default constructor - or - a constructor taking the context.</li>
</ol>
Therefore, you get some powerful feautures :<br />
<ol>
<li>You have dependency injection available in any Activity</li>
<li>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.</li>
<li>You even can have circular dependencies - although a good design should avoid this.</li>
<li>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.</li>
</ol>
<h2>
Step 2 - Making use of Lifecycle Management</h2>
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.<br />
<br />
To get suport of the lifecycle management, simply implement Initializeable or the Destroyable interfaces.<br />
<br />
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.<br />
<br />
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.<br />
<br />
So we are having Our ConfigurationService like this :<br />
<br />
<pre class="java" name="code">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();
}
}
</pre>
<br />
Notice, the destruction function is not called by android natively on exit. You need to wire it explicitly atm.<br />
<br />
<h2>
Finish : Demonstrating the lifecycle </h2>
<br />
To demonstrate bean container shutdown and startup, I modified the button callback :
<br />
<br />
<pre class="java" name="code">package org.baracus;
// ...
public class HelloAndroidActivity extends Activity {
// ...
public void onButtonTestClicked(View v) {
customerService.testService();
bankAccountService.createAndOrDumpAccount();
ApplicationContext.destroy(true);
ApplicationContext.initApplicationContext();
}
}
</pre>
<br />
<br />
That's all. Now how are able to manage bean lifecycles with the container :)
<br />
<br />
Next Tutorial : <a href="http://baracusframework.blogspot.de/2013/10/baracus-from-scratch-part-5-advanced.html">Advanced persistence mapping</a>MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-28104575055254943002013-10-21T07:20:00.001-07:002013-10-21T07:22:28.411-07:00BARACUS from Scratch : Part 3 - Persistence Basics<h2>
<b>This page has moved <a href="http://baracusframework.blogspot.de/2013/10/baracus-from-scratch-part-3-persistence.html">here</a></b></h2>
MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-64930535468543220602013-10-20T01:34:00.002-07:002013-10-20T01:34:33.677-07:00Version 0.6 releasedVersion 0.6 stable now is released.<br />
<br />
It contains several bugfixes and introduces an extensible validation system. Sources can be found on <a href="https://github.com/gorefest/baracus-framework/tree/0.6-release-version">Github</a>.<br />
<br />
A quick demo for the bean validation can be found in the <a href="https://github.com/gorefest/baracus-demoapp">Demonstration Application</a><br />
<br />
A tutorial is going to follow, soon.<br />
<br />
(mnt)MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0tag:blogger.com,1999:blog-2391490036482751569.post-34154216226597045022013-10-19T02:07:00.002-07:002015-03-18T12:07:57.785-07:00BARACUS from Scratch : Part 3 - Persistence Mapping Basics<a href="http://baracusframework.blogspot.de/p/baracus-from-scratch-table-of-contents.html"><b>Table of Contents</b></a><br />
<i><br /></i>
<i>Previous Tutorial : <a href="http://baracusframework.blogspot.de/2013/10/baracus-from-scratch-part-2-defining.html">Dependency Injection and IOC</a></i><br />
<i><br /></i>
<i>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.</i><br />
<br />
<b><a href="https://github.com/gorefest/baracus-tutorial/tree/Tutorial-Part-3">Download the sourcecode to this tutorial from Github</a></b><br />
<h2>
Step 1 : The OpenHelper</h2>
<i>If You followed the tutorial from the prior chapter, You can skip this step. Otherwise, just follow the instructions of this step.</i><br />
<br />
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:<br />
<br />
<br />
<pre class="java" name="code">/**
* 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);
}
}
</pre>
<br />
This class needs to be registered to the application context :
<br />
<br />
<pre class="java" name="code">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);
}
}
</pre>
<br />
That's all. Now we can proceed to the next step, defining the entity class.<br />
<br />
<h2>
Step 2 : Defining the entity class</h2>
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:<br />
<br />
<br />
<pre class="java" name="code">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";
</pre>
<br />
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:
<br />
<br />
<br />
<pre class="java" name="code">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);
}
</pre>
<br />
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.<br />
<br />
Finally, I define getters and setters and my entity definition is ready to use:<br />
<br />
<br />
<pre class="java" name="code">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;
}
}
</pre>
That's it.
<br />
<h2>
Step 3 : Defining the Table </h2>
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.<br />
<br />
<i>In a later tutorial, I am going to explain You migr8 and the concept of database model versioning detailled. </i><br />
<br />
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. <br />
<br />
Our database metamodel class (which is an implementation of MigrationStep) is going to look like this :<br />
<br />
<pre class="java" name="code">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;
}
}
</pre>
<br />
<i>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). </i><br />
<br />
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.<br />
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:<br />
<br />
<pre class="java" name="code">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);
}
}
</pre>
Finally, we have to define a DataAccessObject in order to access the entity.<br />
<br />
<h2>
Step 4 : Defining the DAO</h2>
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.<br />
<br />
Notice : Be sure, that the bean has got a public default constructor!<br />
<br />
Therefore you have to inherit and implement a BaseDao. The BaseDao is a generic which needs to be parameterized : <br />
<br />
<br />
<pre class="java" name="code">package org.baracus.dao;
import org.baracus.model.BankAccount;
/**
* Created with IntelliJ IDEA.
* User: marcus
*/
public class BankAccountDao extends BaseDao<bankaccount> {
/**
* Lock the DAO of
*/
public BankAccountDao() {
super(BankAccount.class);
}
@Override
public RowMapper<bankaccount> getRowMapper() {
return null; // TODO : implement
}
}</bankaccount></bankaccount></pre>
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 :<br />
<br />
getAffectedTable simply returns the BankAccount's table constant, getFieldList returns the BankAccount's FieldList constant.<br />
<br />
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 :<br />
<br />
<br />
<pre class="java" name="code">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;
}
}
</pre>
<br />
<br />
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.<br />
<br />
<pre class="java" name="code">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;
}
}
</pre>
<br />
<b>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 <u>setTransient(false)</u>! </b><br />
<br />
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.<br />
<br />
Finally, we have to register the dao bean in the application context:<br />
<br />
<br />
<pre code="java" name="name">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);
}
}
</pre>
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.<br />
<br />
<i>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 ;-)</i><br />
<br />
<h2>
Finish : Creating and retrieving data</h2>
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.<br />
<br />
So the service is actually looking like this :<br />
<br />
<br />
<pre class="java" name="code">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());
}
}
}
</pre>
<br />
<br />
and we are going to wire it to the button function in the MainActivity :
<br />
<pre class="java" name="code">// ...
public class HelloAndroidActivity extends Activity {
// ...
@Bean
BankAccountService bankAccountService;
// ...
public void onButtonTestClicked(View v) {
customerService.testService();
bankAccountService.createAndOrDumpAccount();
}
}
</pre>
<br />
<br />
... done! Now You can watch the results in Your logfile :<br />
<br />
<br />
10-18 20:42:34.260: DEBUG/TUTORIAL_APP(12699): CustomerService Hooray! I have been called!<br />
10-18 20:42:34.260: DEBUG/TUTORIAL_APP(12699): BankAccountService No account for FOOBANK was found. I am going to create one.<br />
<div>
<br /></div>
<div>
... second click ...</div>
<div>
<br /></div>
<div>
<div>
10-18 20:42:54.630: DEBUG/TUTORIAL_APP(12699): CustomerService Hooray! I have been called!</div>
<div>
10-18 20:42:54.630: DEBUG/TUTORIAL_APP(12699): BankAccountService ACCOUNT FOUND. Id is 1</div>
<div>
10-18 20:42:54.630: DEBUG/TUTORIAL_APP(12699): BankAccountService String value ModelBase{id=1, isTransient=false, tableName='bank_account'}</div>
</div>
<div>
<br /></div>
<div>
Now You can define any persistent class You want. </div>
<div>
<br /></div>
<div>
<i>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!</i></div>
<div>
<br /></div>
<h2>
Follow Up : <a href="http://baracusframework.blogspot.de/2013/10/baracus-from-scratch-part-4-ioc-and-di.html">Dependency injection and IOC</a></h2>
MNThttp://www.blogger.com/profile/11621847507530107079noreply@blogger.com0