ADF: Entity-State vs Post-State

There are lots of blog posts on this but all copy and paste the content of EntityImpl javadoc, which is very obscure. I finally found a sensible explanation in Dive into Oracle ADF, but that is usually buried deep in Google results.

I will restate the Dive into Oracle ADF post here, and will add few finer details.

In DB you can run the DML queries as many times as you want without committing them. When we are saving data to DB the call chain is like – transaction.postChanges() -> entity.postChanges() -> entity.prepareForDML() -> entity.doDML(). The entity’s DML operations depend on the post-state. So, transaction.postChanges() effectively make all entities to write their data to DB. They do that by checking their internal post-states and creating appropriate DML queries based on those states. After the DML action the post-state is updated. So, if before postChanges() if the post-state was STATUS_NEW, then after that it would be STATUS_UNMODIFIED, so that on next postChanges() call it doesn’t try to insert the row again.

Entity-state on the other hand represents the entity’s state irrespective of the fact that the changes has been posted (written) to the DB. So, even if all changes in entity are posted, the entity-state remains STATUS_MODIFIED. Entity-state changes only after commit is invoked on the transaction. Unless and until a transaction is committed the changes done in the transaction is not visible in other transactions (this is a feature of relational DB), so, when entity-state changes we know that those changes are now visible to all transactions.

An example:

#!java
row = vo.first();  /* read a row in from DB.  Both EO states are UNMODIFIED */

row.setAttribute("SomeAttr", someValue); /* After this, both states are MODIFIED */

am.getTransaction().postChanges(); /* Changes are written to DB. Trans not committed yet */
                                   /* After this, post-state is UNMODIFIED. Entity-state is MODIFIED */

am.getTransaction().commit(); /* Transaction committed. After this, both states are UNMODIFIED */

If you see EntityImpl’s javadoc then you will notice that we have one extra post-state – STATUS_INITIALIZED. Entities have this post-state only when it is newly created. The moment one of its attribute is set, this changes to STATUS_NEW. When postChanges() is invoked then DML action is skipped if post-state is STATUS_INITIALIZED. This prevents blank rows from getting inserted. Entity-state never has this value since entity-state is meant to be used for writing business logic, and from that perspective STATUS_NEW and STATUS_INITIALIZED makes no difference.

ADF: EntityImpl.refresh(…) has no effect

Many times we use entity.refresh(REFRESH_FORGET_NEW_ROWS | REFRESH_UNDO_CHANGES) to prevent any changes we made to that entity from getting committed to the DB. What the refresh() method effectively does is change the post state of the entity. (See difference between entity-state and post-state.) The post-state is later used to decided which DML operation to use for the entity, i.e. DML_DELETE, DML_INSERT or DML_UPDATE (ref).

I recently came across a code which called refresh() from the entity’s prepareForDML() but that had no effect. ADF documentation says nothing about such a behaviour. I read the EntityImpl code and got my answer there. When we are saving data to DB the call chain is like – transaction.postChanges() -> entity.postChanges() -> entity.prepareForDML() -> entity.doDML().

prepareForDML() and doDML() have first argument operation; this is the DML operation to undertake. The code to decide which DML operation to perform based on entity’s post-state is in EntityImpl.postChanges(). That method invokes prepareForDML() method with the DML operation to undertake. Changing post-state from prepareForDML() or doDML() is useless. So, post-state needs to be set no later than postChanges(). However, if you do need to do this from prepareForDML() or doDML() then you could modify the operation param’s value appropriately.

Create a Tag field using Django-Select2.

The excellent framework – Select2, have had support for tags for a long time, but Django-Select2 lacked that, until now (version 4.2.0).

Tag fields are very much like any multiple value input field except that it allows users to enter values which does not exist in the backing model. So, that the users can add new tags.

For this purpose few new fields have been added to Django-Select2. They all have the suffix – TagField. Few widgets too have been added to auto configure Select2 Js to run in “tagging mode”.

You can see the full reference in docs – http://django-select2.readthedocs.org/en/latest/ref_fields.html and http://django-select2.readthedocs.org/en/latest/ref_widgets.html.

Simple tag field implementation example

You can find this code in testapp too.

models.py:-

#!python
class Tag(models.Model):
    tag = models.CharField(max_length=10, unique=True)

    def __unicode__(self):
        return unicode(self.tag)

class Question(models.Model):
    question = models.CharField(max_length=200)
    description = models.CharField(max_length=800)
    tags = models.ManyToManyField(Tag)

    def __unicode__(self):
        return unicode(self.question)

forms.py:-

#!python
class TagField(AutoModelSelect2TagField):
    queryset = Tag.objects
    search_fields = ['tag__icontains', ]
    def get_model_field_values(self, value):
        return {'tag': value}

class QuestionForm(forms.ModelForm):
    question = forms.CharField()
    description = forms.CharField(widget=forms.Textarea)
    tags = TagField()

    class Meta:
        model = Question

Above I am trying to create a form which the website users can use to submit questions. Things to note in TagField is that it is almost like we use any other AutoModel fields in Django-Select2, except that here we override one new method get_model_field_values().

When users submit a tag field, the usual validation runs, but with a twist. While checking if the provided value exists, if it is found not to exist then the field, instead of raising error, creates that value. However, the library does not know how to create a new value instance, hence it invokes create_new_value() to do that job.

Model version of the tag field already knows that it is dealing with Django models so it knows how to instantiate that, but does not know what values to set for the attributes. So it implements create_new_value() which in-turn invokes get_model_field_values() to get required attribute names mapped to their values.

Before you continue

One key thing to remember is that unlike other widgets, you ideally should not be using tag widgets with normal Django fields. Django fields do not allow creation of a new choice value. However, if you want tagging support but do not want to allow users to create new tags then you can very much use the tag widget here and pair it up with normal Django fields. However, that may not be a good idea, since then UI would still allow creation of new tags, but on submit the user would get an error.

Making it better

We can make this better and interesting. A typical tagging system should have the following features.

  • Ability to detect misspellings. Peter Norvig’s essay on this is excellent. More information can be found on Stack Overflow.
  • Use statistics to order the results. This very much useful when the tag counts ballon up. The statistics could be based on how many tags are frequently used together. Of course when you start a site you would not have any data, in that case for a period of time you can set the algo to only learn.
  • Cache frequently used tags. This is a normal optimization technique which is frequently used. A memcache like layer is usually used to cache the DB data, and if the data is not found there, then a DB hit is made.

Find one’s complement of a decimal number.

One’s complement of a binary number (1001_{2}) is (0110_{2}). That is, we simply flip the bits. This is easy to do in binary. However, if the number is in base 10, then do we do that. Convert that to base 2 then flip the bits and change it back to base 2? Maybe, but if the number is stored in native data types (like int) then we can simply use bitwise not to invert the bits, without any conversion.

In my case it wasn’t so simple. I had a really big number, which had to be stored in BigDecimal. Converting to base 2, back and forth too is very costly. So the efficient algo for that is…

(x’) is the one’s complement of (x) and it has (b) bits, or we are interested in only (b) bits of it.

$$ \begin{align} &x + x’ = 2^{b+1} – 1 \tag{all ones for all b bits} \ &\Rightarrow x’ = 2^{b+1} – 1 – x \ \end{align} $$

So, the equivalent Java code for the above is…

public static BigDecimal onesComplement(BigDecimal n, int totalBits) {
    return new BigDecimal(2).pow(totalBits + 1).subtract(BigDecimal.ONE).subtract(n);
}

Lessons learned from PhoneGap (Cordova) and jQueryMobile on Android

I recently created my first Android app – JustTodo. This is a simple app but took considerable time to finish due many unexpected problems. Google wasn’t too helpful either. Here is a list of issues I faced and their solutions I discovered.

General tips

Zooming and Scaling

I presume that you have already encountered suggestions over the net that you should use meta tag in your html page. So, that your page is scaled to, maybe, take the exact device width. For example you may use…

<meta name="viewport" content="width=device-width, initial-scale=1.0">

width=device-width will make sure that your page’s width is equal to the device’s width. Of course, for any device, the width differs based on if it is in landscape or portrait orientation.

initial-scale=1 will hint the mobile browser to not zoom out the pages. Mobile browsers typically zoom out the whole page when it is initially loaded, so that the whole page fits on the screen. This way the user can tap anywhere to zoom into that exact location. You do not want that to happen to your apps.

However, in spite of the above setting, the browser may still scale your page. This is because a web page designed for iPhone 3G will look half its size on iPhone 5, since iPhone 5 has twice the pixel density. To prevent the webpages from breaking on high DPI devices, the devices usually scale them by a fixed percent to make them look like they would on MDPI (Medium Dots Per Inch) devices. Webpages can read the value of window.devicePixelRatio to know the factor by which they have been scaled. This is 1 for MDPI devices.

jQueryMobile tips

Do not minify jqm structure css!

I don’t know the reason or the root cause for this. When I use the jquery.mobile.structure css minified by Yahoo UI compressor, the fixed header stops resizing on orientation change! The solution is simple. Always use the official minified version.

tap event gets fired when swipe is fired

If on an element if you are listenning for both tap and swipe events, then it would be better to replace tap by vclick. This is because tap is fired before swipe. However, in case of vclick, it waits for about 200 – 300ms to check if any other event is fired. If not then it fires that. This way you can prevent user accidentally clicking an element while trying to swipe it.

Better swipe event detection

Jqm sets swipe horizontal distance threshold to 30px and time threshold to 1s. Which means to successfully swipe an element the user needs to drag his finger at least 30px horizontally within 1s. I usually set the time threshold to 2.5s. However, due to scaling on high density bigger phones and tablets the physical distance covered by 30px on a MDPI and a XHDPI might vary by a lot. This would force the users to drag their fingers for longer distance in the same duration. So, the trick is to change the distance threshold, such that it covers the same physical distance on all devices.

I wrote the following Javascript function for that.

#!javascript
function getLenInCurrDevice(len) {
    var refernecDPI = 192.2960680247461, // Ref device is Sony Xperia Mini Pro - SK17i.
        pRatio = window.devicePixelRatio, // Ratio of current device DPI on a square inch to that of a MDPI.
        currDPI = refernecDPI * Math.sqrt(pRatio),
        originalWInInch = len / refernecDPI;
    return (originalWInInch / pRatio) * currDPI;
}

For a given distance in px the above function will return a new distance in px on the current device, such that, the distances cover the same physcial length on the current and the reference devices. In this case the reference device is Sony Xperia Mini Pro Sk17i, which has DPI of about 192.3 and devicePixelRatio of 1.

If you want to accurately calculate the DPI of your device you can use the DPI Calculator here.

Cordova tips

Caution when using Proguard

Build files created for an Android project which allows you to enable Proguard on your project. Proguard analysis the Java files and removes all which are not used. However, it also strips out the Cordova plugin classes, since it does see them referenced in any Java classes. (They are referenced from the cordova.js file.) So, you need to add the following to your Proguard config.

-keep class org.apache.cordova.** { *; }

Minimum plugins needed

This is not documented anywhere and it seems the minimum plugins needed are – App and Device. Frankly, I did not try removing them ever. So, maybe even they too are needed. Just try it and let me know. 😉

Although I must mention that if you remove NetworkStatus plugin then occasionally you might see error related to that in the console. Other than that there is no adverse effect of that. In my app I have kept this disabled, so that I can create an app which requires no special permissions. 🙂

Remove junk files and folders

Be sure to delete assets/www/res, assets/www/specs and assets/www/specs.html files and folders. The first one might be about 7MB! Actually the only file needed is cordova.js and the cordova.jar file.

Show native Android context menu

In JustTodo the user when long presses an item in the list it programmatically shows the context menu from the JS. There are two parts to this problem. First is adding the code which allows the JS to open the context menu. Second is to prevent WebView from automatically opening the context menu. More on this later.

Implementing context menu

First step is creating the context menu xml. Below is an example.

res/menu/example_ctx_menu.xml. The xml’s name can be of your choosing.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:id="@+id/edit"
          android:title="@string/edit"/>
    <item android:id="@+id/delete"
          android:title="@string/delete" />    
</menu>

res/values/strings.xml. This maps the key we used in menu xml to the actual string which is displayed to the user.

<?xml version='1.0' encoding='utf-8'?>
<resources>
    <string name="edit">Edit</string>
    <string name="delete">Delete</string>
</resources>

The official way we should be implementing this is using Cordova Plugins. However, I find the technique described here to be simpler. You be your best judge.

NativeContextMenu.java

#!java
public class NativeContextMenu {
    private WebView mAppView;
    private DroidGap mGap;

    public NativeContextMenu(DroidGap gap, WebView view) {
      mAppView = view;
      mGap = gap;
      mGap.registerForContextMenu(mAppView);
    }

    @JavascriptInterface
    public void showCtxMenu() {
        mGap.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mGap.openContextMenu(mAppView);
            }
        });
    }

    private void raiseJSEvent(String event) {
        mGap.sendJavascript("$(document).trigger('" + event + "');");
    }

    boolean onContextItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.edit:
            raiseJSEvent("menu.item.edit");
            return true;
        case R.id.delete:
            raiseJSEvent("menu.item.delete");
            return true;
        }
        return false;
    }

    void onCreateContextMenu(ContextMenu menu, View v,
                                    ContextMenuInfo menuInfo) {
        mGap.getMenuInflater().inflate(R.menu.todo_ctxmenu, menu);
        raiseJSEvent("menu.opened");
    }

    void onContextMenuClosed(Menu menu) {
        raiseJSEvent("menu.closed");
    }
}

YourCordovaActivity.java

#!java
public class YourCordovaActivity extends DroidGap {
    private NativeContextMenu ctxMenu;

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        super.loadUrl(Config.getStartUrl());

        ctxMenu = new NativeContextMenu(this, appView);
        appView.addJavascriptInterface(ctxMenu, "ContextMenu");
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        return ctxMenu.onContextItemSelected(item) ? true : super.onContextItemSelected(item);
    }

    @Override
    public void onContextMenuClosed(Menu menu) {
        super.onContextMenuClosed(menu);
        ctxMenu.onContextMenuClosed(menu);
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v,
                                    ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        ctxMenu.onCreateContextMenu(menu, v, menuInfo);
    }
}

Now ContextMenu.showCtxMenu() would be available to you in Javascript.

example.js

#!javascript
$('element').on('taphold', function  () { // taphold event is defined by jqm
    ContextMenu.showCtxMenu(); // Shows the context menu.
                               // Also the user will get a haptic feedback.
});

$(document).on('menu.item.edit', function () {
    console.log('Edit option was selected.');    
});

Preventing WebView from automatically opening the context menu

The big problem you will face here that when you long press, the context menu will open twice. One by your call in JS code, and another by WebView. WebView has a method setLongClickable() which even if you set after calling registerForContextMenu() does not seem to have any effect. WebView directly calls performLongClick() without checking if isLongClickable(). So the other way to do this is make NativeContextMenu also implement OnLongClickListener.

Changed codes.

NativeContextMenu.java

#!java
public class NativeContextMenu implements OnLongClickListener {  // <---
    private WebView mAppView;
    private DroidGap mGap;

    public NativeContextMenu(DroidGap gap, WebView view) {
      mAppView = view;
      mGap = gap;
      mGap.registerForContextMenu(mAppView);
      mAppView.setOnLongClickListener(this); // <---
    }

    @JavascriptInterface
    public void showCtxMenu() {
        mGap.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mGap.openContextMenu(mAppView);
            }
        });
    }

    private void raiseJSEvent(String event) {
        mGap.sendJavascript("$(document).trigger('" + event + "');");
    }

    boolean onContextItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.edit:
            raiseJSEvent("menu.item.edit");
            return true;
        case R.id.delete:
            raiseJSEvent("menu.item.delete");
            return true;
        }
        return false;
    }

    void onCreateContextMenu(ContextMenu menu, View v,
                                    ContextMenuInfo menuInfo) {
        mGap.getMenuInflater().inflate(R.menu.todo_ctxmenu, menu);
        raiseJSEvent("menu.opened");
    }

    void onContextMenuClosed(Menu menu) {
        raiseJSEvent("menu.closed");
    }

    @Override
    public boolean onLongClick(View v) {  //<---
        return true; // We return true, to let performLongClick() know that we handled the long press.
    }
}

The only side effect of the above code is that whenever the user long presses and you do not show a context menu, the user will still get the haptic feedback. The only way to circumvent that is by sub-classing CordovaWebView and overriding performLongClick().

How to register your Aadhaar number with your bank (ICICI) without the physical Aadhar card

If you are one of those unfortunates who need to register their Aadhaar number with your bank to get LPG gas subsidy, but you lost your Aadhaar card or did not receive that, then read on…

About two years back I enrolled for Aadhaar. I never received that, but I did not bother checking, since in those days Aadhaar was mostly like a passing fad. Well, now it is different. We have probably three more months of grace period before this abomination kicks in.

I went to ICICI bank with my e-Aadhaar print out, but they refused to accept that, citing company directive to accept only physical cards. This was done to prevent fraud. I contacted Aadhaar “Customer Care” to request for a duplicate physical card, but was informed that there is no such provision! In fact they have been informed that e-Aadhaar should be accepted in lieu of Aadhaar card.

I had hit a stone wall. The only way I could resolve this by trying to reason with ICICI (my bank). We cannot reason with the Govt., which keep on inventing nasty little things to make our life difficult. I tried sending my concern via https://infinity.icicibank.co.in/web/emailus/jsp/emailUs.jsp but that ended up in internal server error. Nice! (sarcasm)

I let the issue rest for sometime. After reading about the LPG deadline again in the newspapers I shook myself up, and started finding alternative ways to get in touch with ICICI. During my hunt I ended up with the email id of the CEO. I mailed to her my plight and requested her to invent an alternative process for people like us. After all, if the aim is to prevent fraud then there should be other ways too. Also they are our banker who already know so much about us to form an informed likely-hood of fraudulent stunts we may try.

I was pleasantly surprised to hear back from a Senior Manager within one business day. She assured me they will address this. On the second day I got a call from my bank Branch Manager that a new process has been sanctioned and I need to bring my Aadhaar Enrollment slip with me. Yeeaah! The Manager used the Enrollment slip to download my e-Aadhaar from uidai.gov.in directly onto his system. Some form fillings and photocopies of an Id proof and I was done.

So, if you have an account in ICICI then you can use the above process. You might want to directly check with the Branch Manager if the bank employees insist that original Aadhaar card is a must and there is no other way. They may not be informed. This process came into effect on 21st May, 2013.

If you have account in other banks and they do not yet have such a provision then you might want to communicate with the bank senior management. I was informed by a reliable source that the banks received a circular in March of this year from Govt., that e-Aadhaar should be accepted in lieu of original Aadhaar card.

I also got another info that you can download your e-Aadhaar for a maximum of five times. I am not sure how true is this, but I am not keen on testing this myself. 😛

BTW, for your information I will reiterate that – there is no way as of now to request for duplicate Aadhaar card.

Anyway, happy enrolling your Aadhaar. BTW do not forget to take your mobile phone too since you will receive your OTP on your registered phone when then bank employee tries to download your e-Aadhaar.

Update 26-Aug-2013 I am an Indane LPG customer and when I checked my Aadhaar status on their site, it showed Red for LPG and White (Invalid/Unkown) for Bank. I seeded my LPG information online from https://rasf.uidai.gov.in. Also I created an account on Indane and there they again ask for the Aadhaar details, which I provided.

Update 2-Sep-2013 Now Indane site shows Green for LPG side and Bank side is Red! I already seeded by Aadhaar with bank in May. I followed up with ICICI customer care and got it confirmed that they have my correct Aadhaar number. They suggested, I check with the Gas agency, since enrolling online may not be same as enrolling offline.

Update 6-Sep-2013 Went to my Gas agency. According to them my Aadhaar is already enrolled. So, it seems enrolling online is enough. Now I am stuck again. My guess is that the Indane software checks the status in its own database. Maybe it sycns the Bank linkage details on monthly basis for accounts with valid LPG linkage. I will now wait till the end of this month. If it becomes Green till then, fine, else I will have to pay a visit to my bank.

Update 18-Sep-2013 Finally! Now Indane site shows that both IOCL and bank are linked. It is showing green for both now. I have been checking almost regularly, so it took around 15 days to refresh.

Update 10-Oct-2013 I received my first DBT (Direct Bank Transfer) subsidy! The subsidy was credited one day before I received SMS from Indane that cash memo has been generated. However, the amount is Rs.435 only. This time they charged Rs.1096 (plus Rs.20 extortion by delivery boy) so the net cylinder refill price comes to Rs.661! That is damn costly. Few months back it was less than Rs.500. PS. The subsidy from Indane was credited by the name ‘APBS_IOC R_07-10-2013’, so look for similar name in your bank statement.

Update 18-Dec-2014 Today I finally received my Aadhaar card! That is, I received it after around 3.5+ years later!!! Kudos to Indian bureaucracy, they do get things done, even though it may not matter by then.