Thursday, 23 August 2012

Creating Good Log Messages

Sometimes a bug emerges at a customer site and the support staff sends me a log session and a set of recreation steps to figure out the bug. With rich log messages, that provide indication of what and where the application failed, am able to understand the bug and ultimately fix it. This has lead me to understand how important log messages are in an application.

There are tones of article emphasising on the importance of good programming practices etc but very little about logging practices. On this post am going to describe a good log message based on my experience.

To start with, a few rules that you should follow when logging;

  1. Log messages should be short and precise.
  2. Avoid spelling mistakes in the logs.
  3. Provide fatality levels in your messages. This will help the support analyst to rate the bug/issues severity.
  4. Avoid fancy characters in the log messages .
  5. Avoid logging your messages to the database, this cost space and reduce performance.
  6. Always have a consistent format for your logs. this helps is keeping uniformity

Here is how a good log message should appear:

ERROR|| DATETIME: 22-Aug-2012 13:24:24 || DESCRIPTION: Could not write to output file 'C:\thisfolder\myfile\report1.txt', 'The directory name is invalid.' || SOURCE: Report.SaveReport || PROCESSID: 3456 || PCNAME: Mark-laptop||  USER: Mark || BUILD VER: 2.1.1 || OS :  Windows 7 Version 6.1 Build 7600

 

LOG MESSAGE BREAK DOWN

You will realise I have used a double slash to segment my message, this is to make it readable. Lets break down the sample message and look at it bit by bit.

SERVERITY– This is the first section of the message indicating ERROR. It is very helpful in determining the importance of the message at a glance.

DATETIME: It is the date and time when the error occurred. This is important in case you would like to know the frequency of error or even tag it to a certain external process or event.

DESCRIPTION:  This is a test message of what happened. This can be the runtime  message generated by the debugger or a custom message by the developer.

SOURCE: The code location that generated this message. This will save you a lot of time figuring out where you need to look in your code. In my case I have used the class_name.method_name format.

PROCESSID: In case you have several processes it good to know which particular one is being a nuisance.

PCNAME: This gives you the machine where the error occurred. You could also provide the server name just incase your logs come from different hosts.

USER: An identifier of the user logged in, this is important if you want to track a users actions within the session. You could use a session id instead of a username like I have done.

BUILD VER: This is the code version running. This is very important as the location in the log may be for a different version of code from what is currently running.

OS: Some bugs are tagged to Operating Systems therefore it is important o know what platform the user was on.

 

Most of the time we rely on the debugger to fix our bugs but suppose a bug occurs in site and you manage to fix it through the information provided by the logs. This is the reason why we should always improve the content to provide as much information as possible.To do this think about what information the log provided, evaluate if was it was helpful and find out if more content be appended to make it even more helpful.

Making the log rich enough may not be your priority because  as the developer you have the debugger,  but it a very important aspect for the support team and the customers too. You will be shocked at how may issues the support team can resolve at site just because they can make out what the problem is. This makes it easier for you as a developer and you can concentrate on more relevant issue.

Those are my thought about log messages. If you may have anything to add please drop me a comment.

Thursday, 26 July 2012

SQLite Database in Android

SQLite is a database embedded in Android OS, making it of course the default Android DB. The reason why it is a perfect choice for most mobile OSes including android is because its compact, configuration is not required, it’s serverless, thus making it a lightweight DB.

What are the things you should know about SQLite before we jump into its implementation on Android:

  1. SQLite supports standard relational database features like SQL syntax, transactions and prepared statements.
  2. Only Three datatypes are supported in SQLite; Text, Integer and real (Double). All other type need to be converted to these types.
  3. Data type integrity is not maintained in SQLite such that you can write an integer into a string column and vice versa.
  4. Referential integrity is not maintained, meaning there is no Foreign Key constrains or Joins statements.
  5. SQLite Full Unicode support is optional and not installed by default.

For demonstration purpose’s, am going to create simple students database application. My Application will contain two tables; Students Table and a Department table.

Student Table columns:

  • StudentID
  • FirstName
  • SurName
  • DateOfBirth
  • DeptID

Department table columnts

  • DeptID
  • DeptName

 

SQLite on Android doesn’t have a management interface to create and manage databases from, so we are going to do this though code. To start with, we shall create a class to handle the Database and table creation. Here is the structure of the class:

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class StudentsDataHelper extends SQLiteOpenHelper  {
   
   

//Tables Constants
private static final String Table_Students="Table_Students";
private static final String Table_Dept="Table_Dept";

//Student table columns Constants
private static final String Col_Studentid="StudentID";
private static final String Col_FirstName="FirstName";
private static final String Col_Surname="Surname";
private static final String Col_DateOfBirth="DateOfBirth";
private static final String Col_Stud_DeptID="DeptID";

//Dept table columns Contants
private static final String Col_DeptID="DeptID";
private static final String Col_DeptName="DeptName";

    private static final String DATABASE_NAME = "Students.db";
    private static int DATABASE_VERSION=1;

    public StudentsDataHelper(Context context){
        super(context, DATABASE_NAME,  null, DATABASE_VERSION);

       
    }
   
     @Override
    public void onCreate(SQLiteDatabase db) {
        //TODO Create table Student TABLE
        //TODO Create table Department TABLE
    }
    
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //Drop the Student Table
        //Drop the Department Table
        onCreate(db);
    }
   
}

 

The StudentsDataHelper class Inherits the‘SQLiteOpenHelper’ class . This class provides us with two override methods; onCreate() which is called by the framework when the database doesn’t exist and onUpgrade() called when the database version is increased in the application code, This method allows one to upgrade the database schema.

In the constructor , the Super() method of ‘SQLiteOpenHelper’, requires the following parameters:

  •  Context:-This is the context to which the database is attached.
  •  DatabaseName:- The name of the Database.
  • CursorFactory:- This can either be used as a tandard cursor which acts as a pointer to the results returned from the database. Or a Specialized Cursor done by extending the Cursor class to do additional operations or verifications on the database result.
  • Version:- The version of the database schema. The constructor creates a new blank databse with the specified name and version.

 

OnCreate() Method

On our overriden onCreate() method we shall put the standard SQL statements to create out table and execute them against the Database: Here it is;

@Override
public void onCreate(SQLiteDatabase db) {
     String CREATE_STUDENT_TABLE=String.format("CREATE TABLE IF NOT EXISTS %s " +
             "(%s INTEGER PRIMARY KEY AUTOINCREMENT,%s TEXT,%s TEXT,%s TEXT,%s INTEGER)" ,Table_Students ,Col_Studentid, Col_FirstName, Col_Surname, Col_DateOfBirth,Col_Stud_DeptID);
    
     db.execSQL(CREATE_STUDENT_TABLE);
    
     String CREATE_DEPT_TABLE=String.format("CREATE TABLE IF NOT EXISTS %s (%s INTEGER PRIMARY KEY AUTOINCREMENT,%s TEXT)" ,Table_Dept ,Col_DeptID,Col_DeptName);
    
     db.execSQL(CREATE_DEPT_TABLE);
    
}

 

On this method you can create the tables, views and triggers. It is usually invoked only once when the database does not exist or the first time the application is ran. You will also realise later that I have invoked the method gain during the upgrade.

 

 onUpgrade() Method

Sometimes you want to change the Database schema; Add new table, Add columns, change datatypes. to do this you will require to override the onUpgrade() method:

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    db.execSQL("DROP TABLE IF EXISTS " + Table_Students);
    db.execSQL("DROP TABLE IF EXISTS " + Table_Dept);
   
    //Drop any view or triggers if any
    onCreate(db);
}


In our method, we drop all the tables and the call the method onCreate()  to recreate our schema again. For this method to be executed the database version in the constructor will have to change.

private static int DATABASE_VERSION=2;

For my simple case here, I have dropped all the table but what if there is data that you want to conserve in the tables? You can create temporary tables and preserve the data  then retrieve it after you are done with the upgrade.

Friday, 29 June 2012

Where should new items appear in a listview?

Today I got a bug logged from QA which got me thinking a little bit.  To start with, the bug was about where the new items were appearing after adding them to the list. I have a listview where I the user add, edit and order items, however, whenever a new item is added, it appears at the top of the list. This is one of those issues you throw to the Design Analyst but then I decided to think through it though there was a potential risk of the designer saying “No this is not how it should be..”.

Back to my Issue; What is the best practice while adding items to a listview, should the item appear at the bottom or at the top of the a list?
I think the answer to this highly depends on the several things, One of the things is the sorting order. If the list has an existing sort, then it should be maintained; example if an associated timestamp (assuming that's the order) is chronological, then the new item will appear at the button and vice versa for the reverse chronological ordering. So if there is a sorting order then insert according to the sorting rules. In situations where the user has scrolled to a point that the first/last items cannot be viewed then auto-scroll to the new item and put the new item into context. The same should apply if the list is paged.

If you consider a scenario where the sort is not applied, then you might want to check what the other parts of the application do. This will allow you to maintain conformity within the application,  in addition, the user can intuitively tell where to find a new entry because of the consistency all through. This how I fixed my bug.

Some schools of thought would argue that adding the item at the top would be a good idea. It gives the user an immediate feedback that the item has been added to the list and reduces on the challenge of keeping focus at the bottom of the list.

Monday, 25 June 2012

How to Send an extended ArrayList through an intent


In this article am going to look at how you can pass a custom list from one activity to another.  This being one of the things I had to deal with in my not complete android application, well, I thought it was worthwhile blogging about it.

Let’s first look at what my code supposedly looks like before we jump into what is happening. 

I have Customer class:

import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;


class Customer implements Parcelable {
       
        private int iProperty1;
        private String sProperty2;
       
        public Customer(){
              super();
        }
       
        public Customer(Parcel in){
              super();
              iProperty1 =in.readInt();
              sProperty2=in.readString();
        }
       
          public int Property1() {return iProperty1;}
          public void Property1(int value) {this.iProperty1 = value;}
         
          public String Property2() {return sProperty2;}
          public void Property2(String value) {this.sProperty2 = value;}
         
          @Override
          public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(iProperty1);
            dest.writeString(sProperty2);
                 
          } 
         
          @Override
          public int describeContents() {
          return 0;
          }
         
          public static final Parcelable.Creator<Customer> CREATOR= new Parcelable.Creator<Customer>() {
            public Customer createFromParcel(Parcel in) {
                  return new Customer(in);
            }

            public Customer[] newArray(int size) {
                  return new Customer[size];
            }
          };
         
         
  }


A customer List,

import android.content.Context;
import java.sql.Array;
import java.util.ArrayList;


class CustomerList extends ArrayList<Customer>{
       private Context context;
       private CustomerDataSource oDataSrc;

       
       public CustomerList(){
             super();
       }
       public CustomerList(Context context){
             super();
             this.context=context;
             oDataSrc = new CustomerDataSource(context);
       }
       
       public void Save(){
            try{
            for(int iItem=0; iItem<this.size(); iItem++ )
            {
                  Customer oCustomer=this.get(iItem);
                  if (oCustomer.IsDirty()){
                        oDataSrc.Save(oCustomer);
                  }
            }
            }
            catch(Exception e){
                  Log.e("ERROR", e.toString());
                  e.printStackTrace();
            }
       }
       public void Load(){
             try{
                  this.addAll(oDataSrc.GetCustomers());
             }
             catch(Exception e){
                        Log.e("ERROR", e.toString());
                        e.printStackTrace();
             }
       }

       public Boolean RemoveCustomer(Customer oCustomer){
             try{
                  oDataSrc.Delete(oCustomer);              
                  this.remove(oCustomer);
                  return true;
             }
             catch(Exception e){
                        Log.e("ERROR", e.toString());
                        e.printStackTrace();
                        return false;
             }
       }
}

Lets begin with the customer class, I know you have realized that it has implemented Parcelable interface. Parcelable is an extremely efficient, low level Protocol which serializes your objects into byte stream. Two methods have also been overridden for similar purpose;

  • writeParcelable(Parcelable, int) -

This method flattens the object data to the Parcel, allowing that object to be reconstructed from the appropriate class loader when later reading. Each element in the object should be parceled individually as I have done with the two class properties.

  • describeContents() -This defines the kind of object you are going to parcel.

To reconstruct the object I have defined a constructor, which will read data from the parcel and map it back to the object.
  public Customer(Parcel in){



I also have an class "class CustomerList" which extends an ArrayList. This basically loads all the customers data from the repository and saves back in. This ArrayList object will be used to sustain the customers data within different activities which was also a bit of  a pain. So how do I do this?



CustomerList oCustomerList = new CustomerList(this);

oCustomerList.load(); // Load all the customers

Intent intent  =  new Intent(this, CustomersActivity.class); //Declare an Intent
intent.putParcelableArrayListExtra("oCustomerList", oCustomerList); //send the Customer list to


startActivityForResult(intent, EditREQUEST_CODE); 


Reconstructing  the list, was tricky because after the object was flattened I could not directly parse it back to a type  CustomerList therefore I had to manuver.

Declare my custome arraylist to persist the original customer list ;

CustomerList oCustomerList = new CustomerList();

The declare another Arraylist since the intent will only return a type arrayList.

ArrayList<Customer> oArraylist = extras.getParcelableArrayList("oCustomerList");

//populate the customer list with the data from arraylist

oCustomerList.addAll(oArraylist);

//Clean up
oArraylist.clear;
oArraylist=null;


Wednesday, 6 June 2012

Custom action management in VS2010 setup project


Apart from developing, some of the other things I do are to create builds for the development team. This involves picking latest code changes from the source safe, compiling them into executable form and eventually building it into a distributable package.  Of course, before releasing the distributable I do test it on a clean machine; by clean I mean a machine that has had no developer or any of the development tools installed.  The testing process will involves deploying on different operating system i.e XP and windows7 including mimicking GPO and Image deployment.

One of the things I have realised over the time I have been a Build master, is that this process is very much ignored until the end of the project.  Discussions relating to extra customizing and shipping usually arise at this time; of course most of the time it’s usually too late to start purchasing new tools, train on them and eventually use them.

This leaves us with a problem on how you can make do with the limited tools and time to customize an installation if such a scenario arises.

Visual studio is one of the kits almost every developer has installed in their machines, it comes with a project setup tool that can be used to do quick and easy install though it is limited in scope. The main drawbacks include:

  •  Having a limited number of dialogs to choose from, which can easily run out on a relatively simple install, example, say that you have a need for a further set of textboxes for a piece of information that contextually belongs on its own and does not look correct if tagged onto one of the other dialogs.
  • The information you can enter is limited. Have you ever needed a dialog with 5 radio buttons? What about a pick-list of options?
  • Build Automation; e.g. you cannot put your project and the VS Setup Project on a build machine and have them built automatically.
  • There is no way to designate a custom action to run with elevated privileges


Just to mention a few…

Developing a setup:

In this post I’ll show you how to create a custom action and to use it in a setup deployment project using C# and Visual Studio 2010. For our sample, we will have windows form application and a deployment project which is ready to build an MSI that will deploy the windows form project to program files.

To start will we will add an extra dialog in our setup then include a custom action to edit the Application Config file (*.exe.config ) with the data collected from the textbox in the dialog during installation.


Here is what my solution looks like:



I have added an Installer class to my Form Project. Do this by right clicking on the solution>Select Add> select Class. From the Add New Item dialogbox select Installer Class.

Here is my app.config file


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
       <appSettings>
              <add key="ConnectionString" value="" />
       </appSettings>
</configuration>





I want to edit the value of the’ ConnectionString’ key during installation.

Tuesday, 15 May 2012

Programmatically sharing a windows directory in C#


Sometime you are in a situation where you want to programmatically share a path and unshared it after use. This was a challenge I was faced with while working on an application that accepted user input, and then it would trigger a different process on another terminal.  The second application would then process and save the output on a path selected by the user. After going through the MSDN and a few google searches, here is what I developed

This article demonstrates how to share a directory and set NTFS permissions on it. start by importing the following library and namespaces.

using System.Security.Permissions;
using System.Security;
using System.Security.AccessControl;
using System.Security.Principal;


Create an instance of the group or user you want to share to, in my case here I want to share to everyone.

NTAccount ntAccount = new NTAccount("Everyone");

Get the group’s Security Identifier (SID).

SecurityIdentifier oGrpSID = (SecurityIdentifier)ntAccount.Translate(typeof(SecurityIdentifier));
byte[] utenteSIDArray = new byte[oGrpSID.BinaryLength];
oGrpSID.GetBinaryForm(utenteSIDArray, 0);

Create a trustee instance from the group SID above

ManagementObject oGrpTrustee = new ManagementClass(new ManagementPath("Win32_Trustee"), null);
oGrpTrustee["Name"] = "Everyone";
oGrpTrustee["SID"] = utenteSIDArray;

Create an Access Control Entry object. Give full access to the folder and allow sub folder to inherit

ManagementObject oGrpACE = new ManagementClass(new ManagementPath("Win32_Ace"), null);
oGrpACE["AccessMask"] = 2032127;//Full access
oGrpACE ["AceFlags"] = AceFlags.ObjectInherit | AceFlags.ContainerInherit; //propagate the AccessMask to the subfolders
oGrpACE["AceType"] = AceType.AccessAllowed;
oGrpACE["Trustee"] = oGrpTrustee;

Create a security descriptor; this will contain the security information for the group

ManagementObject oGrpSecurityDescriptor= new ManagementClass(new ManagementPath("Win32_SecurityDescriptor"), null);
oGrpSecurityDescriptor["ControlFlags"] = 4; //SE_DACL_PRESENT
oGrpSecurityDescriptor["DACL"] = new object[] { oGrpACE };

Lastly Share the directory in question and upgrade the security permissions. In my case is C:\testShare\, the Share Name is Test Share

string FolderPath=”c:\testshare\”;
string ShareName=”Test Share”;
string Description=”This save output”;
ManagementClass mc = new ManagementClass("win32_share");
ManagementBaseObject inParams = mc.GetMethodParameters("Create");
inParams["Description"] = Description;
inParams["Name"] = ShareName;
inParams["Path"] = FolderPath;
inParams["Type"] = 0x0; //Disk Drive
inParams["MaximumAllowed"] = null;
inParams["Password"] = null;
inParams["Access"] = oGrpSecurityDescriptor; 
ManagementBaseObject outParams = mc.InvokeMethod("Create", inParams, null);


Unshare the Directory
Create an Object to query the WMI Win32_Share API for shared files

ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from win32_share");
ManagementBaseObject outParams;
ManagementClass mc = new ManagementClass("Win32_Share"); //for local shares

Loop through all the local shares,

foreach (ManagementObject share in searcher.Get())
{
string type = share["Type"].ToString();

if (type == "0"// Check if it’s a DiskDrive                 {
string path = share["Name"].ToString(); //getting share path name

if (path == "testshare"){ //if it’s the testshare folder

outParams = share.InvokeMethod("delete"nullnull); //Delete the shares

if ((uint)(outParams.Properties["ReturnValue"].Value) != 0)
   {
      // unsuccessful do something

   }
else
   {
     // unsharing successful do something
   }
  }
 }
}