Vladimir Vivien's complete blog can be found at: http://vladimirvivien.wordpress.com/
Sunday, April 22, 2012
While developing an Android project, I needed to test an HTTP client service layer running in my application. I wanted to ensure I unit tested features such as ability to send/receive headers, request http resources, and deal with HTTP response codes properly. However, I did not want to run my JUnit tests using an external HTTP server. This would create a dependency on yet another technology that I would have to launch with Maven. Furthermore, using an external server would make it difficult to validate unit test cases from the server side.
MockHttpServer
You may already know that Android’s library comes with a version of Apache’s HttpComponent. While Android promotes the use of HttpComponent for mostly client side development, the provided APIs include the server-side pieces as well. So, you can create your own HTTP server running on Android.
MockHttpServer is a wrapper around the HttpComponent’s HttpService API. MockHttpServer provides a simple interaction point where developers simply provide an implementation of HttpRequestHandler.
The MockHttpServer handles the setup and thread management. The following shows how to use the MockHttpServer to enumerate and test the expected headers from the client:
private final static int SVR_PORT = 8585;
private final static String SVR_ADDR = "http://localhost:" + SVR_PORT;
...
public void testHeaderEnumerations () throws Exception{
// setup mock server and handler for given path
MockHttpServer server = new MockHttpServer(SVR_PORT);
server.start();
server.addRequestHandler("/test1", new HttpRequestHandler() {
@Override
public void handle(HttpRequest req, HttpResponse rsp, HttpContext context) throws HttpException, IOException {
int headerCount = 0;
String headers = "Accept Accept-Charset";
HeaderIterator it = req.headerIterator();
while (it.hasNext()){
Header h = (Header) it.next();
if(headers.contains(h.getName())){
headerCount++;
}
}
Assert.assertEquals(3, headerCount);
}
});
// setup client connection
URL url = new URL(SVR_ADDR + "/test1");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.addRequestProperty("Accept", "text/xml");
conn.addRequestProperty("Accept", "Audio/mpeg");
conn.addRequestProperty("Accept-Charset", "utf-8");
try{
conn.getInputStream();
}finally{
conn.disconnect();
}
server.stop();
}
The top portion of the code sets up the HttpRequestHandler implementation. This is where one specifies how the server would handle the incoming HTTP request. In the implementation above, we are enumerating and validating incoming HTTP headers. The second portion of the code sets up the client call to the MockHttpServer instance. Here, for simplicity, we are using an instance of HttpURLConnection to make the HTTP request to the MockHttpServer.
Link to project - https://github.com/vladimirvivien/workbench/tree/master/android-tutorials/MockHttpServer
Tuesday, April 10, 2012
One of the reasons I created the Clamshell-Cli Framework originally was to develop a text-based console for JMX. Well, it is here. Introducing Jmx-Cli: Jmx-Cli a command-line interface console for JMX. It was developed using the Clamshell-Cli framework (http://code.google.com/p/clamshell-cli/) to prove that the framework was flexible enough to create a useful tool.
Installing Jmx-Cli
- Download the current distribution from https://github.com/vladimirvivien/jmx-cli/downloads.
- Unzip at the location of your choosing (jmx-cli-home).
- Cd to jmx-cli-home
- From the command prompt, type > java -jar cli.jar
If all works ok, you should see:
/##### /###### /## /##
|__ ## /##__ ## | ## |__/
| ## /######/#### /## /## | ## \__/ | ## /##
| ## | ##_ ##_ ## | ## /##/ | ## | ## | ##
/## | ## | ## \ ## \ ## \ ####/ | ## | ## | ##
| ## | ## | ## | ## | ## >## ## | ## ## | ## | ##
| ######/ | ## | ## | ## /##/\ ## | ######/ | ## | ##
\______/ |__/ |__/ |__/ |__/ \__/ \______/ |__/ |__/
A command-line tool for JMX
Powered by Clamshell-Cli framework
http://code.google.com/p/clamshell-cli/
Getting Started
Using Jmx-Cli is straight forward. From the prompt type ‘help’ to get a list of available commands:
exit Exits ClamShell.
help Displays help information for available commands.
ps Displays a list of running JVM processes (similar to jps tool)
connect Connects to local or remote JVM MBean server.
mbean Creates a label for identifying an MBean
desc Prints description for specified mbean.
list Lists JMX MBeans.
exec Execute MBean operations and getter/setter attributes.
Command Hints
You can also get command hints from the prompt by pressing on the tab key. This will list all available combinations of input. You can narrow down the hints by typing a few letters of the command then press tab.
Connecting to a JVM
1. Use the ‘ps’ command to get a list of JMV processes running locally:
> ps 1444 cli.jar 1740 org.apache.catalina.startup.Bootstrap
2. use the ‘connect’ command to attach to a running process
> connect pid:1740
The sequence above connects to a running instance of Tomcat server.
Listing MBeans
Once connected to a JVM, you can query available MBeans that are running in that JVM. Use the ‘list’ command to retrieve lists of MBeans.
> list filter:"Catalina:*"
The command above will list all MBeans running in the “Catalina” domain. You can label the list for future reference by typing:
> list filter:"Catalina:*" label:true
MBean list [Catalina:*]
[$0] Catalina:type=Server
[$1] Catalina:realmPath=/realm0/realm0,type=Realm
…
Describing an MBean
Once you have a list, you can select a bean to describe. The following describes bean with label ‘Catalina:port=8080,type=Connector’:
> desc bean:"Catalina:port=8080,type=Connector"
MBean: Catalina:port=8080,type=Connector (org.apache.catalina.mbeans.ConnectorMBean)
Implementation of a Coyote connector
Attributes:
port : int (rw) - The port number on which we listen for requests
useIPVHosts : boolean (rw) - Should IP-based virtual hosting be used?
redirectPort : int (rw) - The redirect port for non-SSL to SSL redirects
minSpareThreads : int (rw) - The number of request processing threads that will be created
secure : boolean (rw) - Is this a secure (SSL) Connector?
acceptCount : int (rw) - The accept count for this Connector
maxThreads : int (rw) - The maximum number of request processing threads to be created
URIEncoding : java.lang.String (rw) - Character encoding used to decode the URI
modelerType : java.lang.String (r) - Type of the modeled resource. Can be set only once
packetSize : int (rw) - The ajp packet size.
processorCache : int (rw) - The processor cache size.
...
xpoweredBy : boolean (rw) - Is generation of X-Powered-By response header enabled/disabled?
stateName : java.lang.String (r) - The name of the LifecycleState that this component is currently in
allowTrace : boolean (rw) - Allow disabling TRACE method
useBodyEncodingForURI : boolean (rw) - Should the body encoding be used for URI query parameters
secret : java.lang.String (w) - Authentication secret (I guess ... not in Javadocs)
Operations:
destroy():void
pause():void
stop():void
resume():void
start():void
init():void
The same command can be issued using the associated label of the bean from the list (see above):
> desc bean:$28
Execute an Operation
Jmx-cli lets you execute operation on your management beans as well. The next examples shuts down the Tomcat connector for port 8080:
> exec bean:"Catalina:port=8080,type=Connector" op:"stop"
References
1. https://github.com/vladimirvivien/jmx-cli – Jmx-Cli home page
2 http://code.google.com/p/clamshell-cli/ – Clamshell-Cli Framework web site
Monday, March 19, 2012
Sometimes, you try to solve one problem and end up creating a solution for something completely different. Such is the story of Clamshell-Cli. On several occasions I came across the need of a purely console-based JMX shell. Since I did some work in JMX before, I decided to create one that was usable and basically provided some of the more useful features of JConsole. After looking around for a framework to build comand-line shells-based apps, it became clear that I would have to create one (yes, I considered OSGi and runtime implementations such as Felix, but OSGi comes with its own set of constraints that solve problems other than what I wanted to solve).
So I created my own framework to build command-line shell tools. My requirements were simple: create a flexible, component-based, highly-extensible framework that would not get in developer’s way. The API had to be light, easy to learn, and the runtime had to be super simple to use.
Enter: Clamshell-Cli – http://code.google.com/p/clamshell-cli/
Clamshell-Cli Features
- Easy to get started
- Small API footprint with low learning curve
- Ability to build complex CLI tools such as REPL using plugin architecture
- Simple component model that imposes little constraints on your design
- The plugin architecture is designed for extensibility and feature-scalability:
- If you don’t like how the default implementation works, you can change it completely
- Implement the components you want to change and your feature will be included next time the console is restarted
- Each extension point is mapped to a Java type for easy implementation
- Plugins are deployed as simple jar files
- Support for input hints (tab-press at the console)
- Support for input buffer history
The Design
The Clamshell-Cli API is kept simple on purpose. It uses a plugin paradigm based on Java’s ServiceLoader API. The idea is not to make Clamshell-Cli a bloated piece of software trying to handle everything, but rather provide an extensible platform that lets developers build console-based tools by implementing pieces of functionality via plugins.
All major aspects of a working command-line shell are represented by statically defined interfaces. For instance, if you want to change the console prompt, you simply implement the Prompt interface to return the prompt you want displayed. The Clamshell-Cli interfaces include :
- SplashScreen - interface to render the first splash screen of console-app
- ConsoleIO - interface to handle input and output streams
- Prompt - interface to provide command prompt for the shell tool
- InputController - Interface to handle text input at command prompt
- Command - interface to handle action to be taken based on command input
The Default Runtime
When you download the default runtime, you get a basic shell environment with the following directory structure:
- cli.config - Clamshell-Cli configuration file
- cli.jar - the launcher jar file
- clilib - lib files to boot Clamshell-Cli
- lib - place your dependency jars here
- plugins - location for Clamshell-Cli plugin jars
When you start the default runtime, you immediately encounter the SplashScreen plugin and the Pormpt plugin:
.d8888b. 888 .d8888b. 888 888 888 d88P Y88b 888 d88P Y88b 888 888 888 888 888 888 Y88b. 888 888 888 888 888 8888b. 88888b.d88b. :Y888b. 88888b. .d88b. 888 888 888 888 :88b 888 :888 :88b :Y88b. 888 :88b d8P Y8b 888 888 888 888 888 .d888888 888 888 888 :888 888 888 88888888 888 888 Y88b d88P 888 888 888 888 888 888 Y88b d88P 888 888 Y8b. 888 888 :Y8888P: 888 :Y888888 888 888 888 :Y8888P: 888 888 :Y8888 888 888 Command-Line Interpreter Java version: 1.6.0_22 Java Home: /usr/lib/jvm/java-6-openjdk/jre OS: Linux, Version: 2.6.38-10-generic prompt> _
The defaul runtime implements an input controller that interprets the input one line at time. It assumes that the first word of the input is the command. The controller then delegates the handling of the command to one of the registered Command instances. For instance, if you type ‘help’ at the prompt, the InputController a) locates the Command mapped to command word ‘help’, then delegate handling of the command to the HelpCmd plugin:
prompt> help Available Commands ------------------ exit Exits ClamShell. help Displays help information for available commands. sysinfo Displays current JVM runtime information. time Prints current date/time prompt> _
Using the API
To extend the default runtime and add your own command, a developer would simply provide a plugin that implements the Command interface. Package the class as jar that obeys the configuration rules of a Service Loader / Service Provider (see here), then place the jar in the plugins directory. Next time the Clamshell-Cli console is restarted, the command would be available. The following code shows how simple it is to create your own command using the Command interface. Class TimeCmd, shown below, implements the ‘time’ command as part of the default runtime::
public class TimeCmd implements Command {
private static final String NAMESPACE = "syscmd";
private static final String ACTION_NAME = "time";
@Override
public Object execute(Context ctx) {
IOConsole console = ctx.getIoConsole();
console.writeOutput(String.format("%n%s%n%n",new Date().toString()));
return null;
}
@Override
public void plug(Context plug) {
// no load-time setup needed
}
@Override
public Command.Descriptor getDescriptor(){
return new Command.Descriptor() {
@Override public String getNamespace() {return NAMESPACE;}
@Override
public String getName() {
return ACTION_NAME;
}
@Override
public String getDescription() {
return "Prints current date/time";
}
@Override
public String getUsage() {
return "Type 'time'";
}
@Override
public Map<String, String> getArguments() {
return Collections.emptyMap();
}
};
}
}
A quick explanation of the code is in order:
- Method execute() - invoked by the input controller instance when it detects the String time from the command-line. The method retrieves the IOConsole from the context object and use it to print the time. It returns null to the controller (indicating the command did not generate a result).
- Method plug() - a lifecycle method that is invoked by the framework when the command is first initialized. For our example, there nothing to do.
- Method getDescriptor() - returns an instance of interface Command.Descriptor which is used to describe the features and document the Command. For our example, the Descriptor interface is implemented anonymously with the following methods:
- Method Descriptor.getNamespace() - returns a string identifying the command’s namespace. This value can be used by input controllers to avoid command name collisions.
- Method Descriptor.getName() - returns the string mapped to this command object. In our implementation, it returns “time”.
- Method Descriptor.getUsage() - intended to provide a descriptive way of using the command.
- Method Descriptor.getArguments() - returns a Map containing the description for each arguments that may be attached to the command. This example uses none.
Clamshell-Cli Examples
The best way to learn how to use the Clamshell-Cli API is to download the source code and look at how the plugins are implemented. You can also check out:
- Jmx-Cli, a fully-functional JMX command-line tool implemented using the Clamshell-Cli API https://github.com/vladimirvivien/jmx-cli
References
http://code.google.com/p/clamshell-cli/ - Clamshell-Cli Home
http://code.google.com/p/clamshell-cli/wiki/CreatingCommandPlugin - How to create a Command plugin
http://code.google.com/p/clamshell-cli/wiki/DeployPlugins - How to deploy a Plugin
https://github.com/vladimirvivien/jmx-cli - A JMX command-line tool (yes I did build it) – built using Clamshell-Cli (discussed in future post)
http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html - Documentation on ServiceLoader API
http://java.sun.com/developer/technicalArticles/javase/extensible/ - Article on creating extensible application using Java
Saturday, November 19, 2011
When creating apps in Android, you have access to numerous data sources. In fact, Android comes with a built-in instance of SQLite, provides access data to local storage, gives direct access to remote resources over HTTP. In additions to these native data providers, you can setup your own application to be a data provider. Since Android does not allow applications to share data directly with one another, you can make your data accessible to (components in your application or) other applications using the ContentAPI. For instance, Android has a phonebook content provider which lets you search, add, modify contacts.
A ContentProvider is a first-class Android component (like Activity, Service, etc). It is registered in the manifest file like any other high-level components. You can read about Android ContentProviders from Android’s website at:
- http://developer.android.com/reference/android/content/ContentProvider.html – API doc
- http://developer.android.com/guide/topics/providers/content-providers.html – Content Provider usage
The intent of this write up is to present a simple approach to create and work with your own ContentProvider. It is not meant to be an introduction to data storage in Android. If you have not used data storage in Android, visit the Android’s developer web site and look for data storage.
ContentProvider Overview
As mentioned above, one of the primary purposes for the use of ContentProvider is data sharing. Since databases and other internal data stores are application-scoped, there’s no way to share information between applications. A content provider lets you expose access to your application’s data in a structured and uniform manner.
ContentProdviders are considered data access objects (a software pattern). Their backing data store can be a database, data from local storage, data from a remote server over HTTP, or a custom data source. For this write up, I will use the SQLite database as the backing datastore for the sample content provider that will be be demonstrated. This will keep things simple since the API for SQLite maps nicely to the method used by the ContentProvider API.
The Example
How to Do It
- Define data model – figure out what will be in stored
- Create a Descriptor class – to help describe the data that you will work with. This is not part of any of the APIs. It is a class that I have used to help with with creation of providers.
- Create a Database Class – the database class is implemented as a
SQLiteOpenHelperintended to help with creation/management of the database instance itself. - Define your ContentProvider – the content provider will use the classes created above to install the database, access, and manipulate the data in it. This is also the class that Activity classes will use to access the data.
Define the Data Model
IDNAMEADDRESSCITYZIP
It’s good practice to define an ID column as an identifier for the data row. The ContentProvider’s URI mechanism uses the ID to refer to saved data entities.
Descriptor Class
- URI Authority – the authority portion of the URI representing this entity. In our example it is “com.favrestaurant.contentprovider”
- URI Matcher – this is an internal registry used to map a URI path (serviced by the ContentProvider) to an integer value.
- Entity Class – an inner static class that represents the entity to be managed by the ContentProvider. In our example, this class is called Restaurant. It exposes meta data such as the entity name, supported URIs, etc.
- Class EntityClass.Cols – the entity class provides an inner class called Cols. As you may have guessed, this class exposes the name of the columns to exposed by the ContentProvider for the entity.
public class ContentDescriptor {
public static final String AUTHORITY = "demo.contentprovider.restaurant";
private static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY);
public static final UriMatcher URI_MATCHER = buildUriMatcher();
private ContentDescriptor(){};
private static UriMatcher buildUriMatcher() {
final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
final String authority = AUTHORITY;
matcher.addURI(authority, Restaurant.PATH, Restaurant.PATH_TOKEN);
matcher.addURI(authority, Restaurant.PATH_FOR_ID, Restaurant.PATH_FOR_ID_TOKEN);
return matcher;
}
public static class Restaurant {
public static final String NAME = "restaurant";
public static final String PATH = "restaurants";
public static final int PATH_TOKEN = 100;
public static final String PATH_FOR_ID = "restaurants/*";
public static final int PATH_FOR_ID_TOKEN = 200;
public static final Uri CONTENT_URI = BASE_URI.buildUpon().appendPath(PATH).build();
public static final String CONTENT_TYPE_DIR = "vnd.android.cursor.dir/vnd.favrestaurant.app";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.favrestaurant.app";
public static class Cols {
public static final String ID = BaseColumns._ID; // convention
public static final String NAME = "restaurant_name";
public static final String ADDRESS = "restaurant_addr";
public static final String CITY = "restaurant_city";
public static final String STATE = "restaurant_state";
public static final String ZIP = "restaurant_zip";
}
}
}
What’s going on…
- The first thing to notice in the code above is the definition of variables
AUTHORITYandBASE_URI. Together these form the URI that identifies the ContentProvider. The URI is used by Android for registering the ContentProvider as part of the application. As you will see later, a ContentResolver class will locate and use the ContentProvider based on the provided URI. - Private method
buildMatcher()creates an instance ofURIMatcherfor the ContentProvider. - Inner class
Restaurantexposes meta data that defines the Restaurant entity managed by the associated ContentProvider. - Furthermore, inner class
Restaurant.Colsdefine meta values for the columns associated with the Restaurant entity.
If none of this makes sense, read on to see how the Descriptor class is used.
The Database Class (SQLiteOpenHelper)
ContentProvider implementation is a database, we will use the SQLLite API here to define the database. The purpose of class RestaurantDatabase is to create, install, and help manage the SQLLite database. The Android’s ContentProvider API (along with the ContentResolver class) uses this class to run DDL scripts to install and update the database. If you implement the onUpgrade() method and change the version of the database, the database will be upgraded automatically next time the code is executed.
The one notable portion of the code below is its use of the ContentDescriptor class (see defined above) to provide meta data the table and fields used in the database.
public class RestaurantDatabase extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "fav_restaurnt.db";
private static final int DATABASE_VERSION = 2;
public RestaurantDatabase(Context ctx){
super(ctx, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + ContentDescriptor.Restaurant.NAME+ " ( " +
ContentDescriptor.Restaurant.Cols.ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
ContentDescriptor.Restaurant.Cols.NAME + " TEXT NOT NULL, " +
ContentDescriptor.Restaurant.Cols.ADDRESS + " TEXT , " +
ContentDescriptor.Restaurant.Cols.CITY + " TEXT, " +
ContentDescriptor.Restaurant.Cols.STATE + " TEXT, " +
ContentDescriptor.Restaurant.Cols.ZIP + " TEXT, " +
"UNIQUE (" +
ContentDescriptor.Restaurant.Cols.ID +
") ON CONFLICT REPLACE)"
);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if(oldVersion < newVersion){
db.execSQL("DROP TABLE IF EXISTS " + ContentDescriptor.Restaurant.NAME);
onCreate(db);
}
}
}
The ContentProvider Class
ContentProvider class with the logic for data access and update. The ContentProvider API exposes several methods for inserting, updating, querying, and deleting data. The example code only implements the insert() and query() methods. Note the usage of the ContentDescriptor to provide naming and configuration meta data for the ContentProvider.
public class RestaurantContentProvider extends ContentProvider {
private RestaurantDatabase restaurantDb;
@Override
public boolean onCreate() {
Context ctx = getContext();
restaurantDb = new RestaurantDatabase(ctx);
return true;
}
@Override
public String getType(Uri uri) {
final int match = ContentDescriptor.URI_MATCHER.match(uri);
switch(match){
case ContentDescriptor.Restaurant.PATH_TOKEN:
return ContentDescriptor.Restaurant.CONTENT_TYPE_DIR;
case ContentDescriptor.Restaurant.PATH_FOR_ID_TOKEN:
return ContentDescriptor.Restaurant.CONTENT_ITEM_TYPE;
default:
throw new UnsupportedOperationException ("URI " + uri + " is not supported.");
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = restaurantDb.getWritableDatabase();
int token = ContentDescriptor.URI_MATCHER.match(uri);
switch(token){
case ContentDescriptor.Restaurant.PATH_TOKEN:{
long id = db.insert(ContentDescriptor.Restaurant.NAME, null, values);
getContext().getContentResolver().notifyChange(uri, null);
return ContentDescriptor.Restaurant.CONTENT_URI.buildUpon().appendPath(String.valueOf(id)).build();
}
default: {
throw new UnsupportedOperationException("URI: " + uri + " not supported.");
}
}
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = restaurantDb.getReadableDatabase();
final int match = ContentDescriptor.URI_MATCHER.match(uri);
switch(match){
// retrieve restaurant list
case ContentDescriptor.Restaurant.PATH_TOKEN:{
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables(ContentDescriptor.Restaurant.NAME);
return builder.query(db, null, null, null, null, null, null);
}
default: return null;
}
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
return 0;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
}
What’s going on…
The onCreate()method is called when the provider is instantiated (by theContentResolverclass). It is, in turn, used to bootstrap the database via theRestaurantDatabaseclass (see above) instance db.- The
getType()method uses theContentDescriptor.URI_MATCHER(seeContentDescriptorabove) to lookup the MIME type for a given URI. - All of the data access & update methods (including
query(),insert(),update(), anddelete()) take a URI parameter. The URI provides hints such as the entity (and cardinality) being queried or updated. For instance, in our example, if the URI to passed to the query() method looks likecontent://com.favrestaurant.contentprovider/restaurants/*the method will return all restaurant rows in the database. This is accomplished by using theContentDescriptor.URI_MATCHERto determine how to process the URI.
Using the ContentProvider
Once you have all of your pieces in place, you can access the data exposed by the content provider using the ContentResolver. There are certainly more robust ways to use to access data from a ContentProvier. This write up shows the simplest (non-production ready) way of doing it. You should investigate which way works best for your use (see http://developer.android.com/guide/topics/providers/content-providers.html).
public class FavRestaurantActivity extends Activity {
TextView txtName;
TextView txtAddr;
TextView txtState;
TextView txtCity;
TextView txtZip;
ContentResolver contentResolver;
Cursor cur;
SimpleCursorAdapter adapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
...
contentResolver = this.getContentResolver();
}
@Override
public void onStop() {
super.onStop();
if(cur != null) cur.close();
}
private void loadContent() {
cur = this.getContentResolver().query(ContentDescriptor.Restaurant.CONTENT_URI, null, null, null, null);
...
}
private void saveContent(){
ContentValues val = new ContentValues();
val.put(ContentDescriptor.Restaurant.Cols.NAME, (this.txtName.getText() != null) ? this.txtName.getText().toString() : null);
val.put(ContentDescriptor.Restaurant.Cols.ADDRESS, (this.txtAddr.getText() != null) ? this.txtAddr.getText().toString() : null);
val.put(ContentDescriptor.Restaurant.Cols.CITY, (this.txtCity.getText() != null) ? this.txtCity.getText().toString() : null);
val.put(ContentDescriptor.Restaurant.Cols.STATE, (this.txtState.getText() != null) ? this.txtState.getText().toString() : null);
val.put(ContentDescriptor.Restaurant.Cols.ZIP, (this.txtZip.getText() != null) ? this.txtZip.getText().toString() : null);
contentResolver.insert(ContentDescriptor.Restaurant.CONTENT_URI, val);
loadContent();
}
}
What is going on…
- First, let me point out that the code used above to access data from the
Activityclass is not optimal for production. You should optimize any io-bound code that has propensity to hang the UI by using an asynchronous task. Nevertheless, the code presented above uses an instance ofContentResolverto access data managed by the backing ContentProvider. TheContentResolveruses the URI value passed in to select the proper ContentProvider registered in the AndroidManifest.xml file (not shown). -
The
saveContent()shows how you can use the Descriptor class to create an instance of ContentValues (from the ContentProvider API) to save data. Each column is mapped to its value using the ContentProvider to provide the column name.
Conclusion
This write up provides a guide for those of you, brave enough, to use the ContentProvider API directly. I have introduced the Descriptor class as a container to register meta data to describe the data element captured by the ContentProvier. The hope is to make using the ContentProvider API more organized and provide some structure when putting your own data access code together.
Friday, October 7, 2011
There is still a great number of Java developers out there who are not doing web apps. They use the JDK’s Java launcher directly to bootstrap their apps using public static void main() (abbreviated thereafter as PSVM). And if you are one of those developers, you understand the implications of having a large classpath. It is not uncommon to see shell command with no less than a dozen jars listed on the classpath.
Of course over the years many options have been provided to help with this issue. One of the most recent is from Java 6 where you can reduce the length of the command to launch your Java application by specifying wildcards values in the classpath as shown below:
$ java -cp path1/*.jar:path2:/*:path3/*.jar package.name.ClassName
This write up proposes an alternative approach where your code loads your application’s classes programatically. This pattern, named Loader/Launcher, separates the loading of your application’s classes from the booting of your application logic. The idea is to provide your own loader class that will load your classpath then delegates further bootstrapping responsibilities to a launcher class. One benefit of this approach is that your command to launch your application can be reduced to something like this (no matter the size of your class dependency graph):
$ java -jar package.name.ClassName
The Loader/Launcher Pattern
The way that the Loader/Launcher pattern works is to de-entangle class-loading concerns from application execution concerns. The loading of classes is handled by a Main class with a PSVM method. The native Java command-line launcher loads the Main class. The execution of the application is delegated to a launcher class that implements the Launcher interface. The Launcher is instantiated and invoked by the Main class.
To implement this pattern, you will need the following high-level components:
- The Launcher interface that will be used as a starting point for your app.
- The Main class where a PSVM method is defined.
- A Launcher implementation to execute the application.
The Launcher Interface
Implementation of this interface is intended to be the starting point of your application’s bootup process. Instead of starting your application directly in the PSVM method, as is done traditionally, you would relocate the logic for your application’s boot up sequence in a class that implements this interface. When the PSVM method is invoked by the native Java launcher, it would delegate the boot sequence of your application to your Launcher instance (see interface below Listing-1).
public interface Launcher {
public int launch(Object ... params);
}
Listing-1
This is a simple interface with a single method, launch(). The method takes an array of objects that can be used to pass in arguments to launcher. The method’s signature makes easy to maintain the semantic of PSVM when using the Launcher.
The Main Class
The Main class is designed to be the starting point for the native Java launcher by exposing a PSVM method. The role of this class, in the Loader/Launcher Pattern, is summed up below:
It creates and loads the application’s classpath. Internally, it instantiates a ClassLoader that is used to load the application’s classpath from a specified location.
Once the classpath is in place, it creates an instance of Launcher, from the classpath, to boot up the application by calling launch().
Listing-2 shows the content of a Main class.
public class Main {
private static String CLASSPATH_DIR = "lib";
private static String LIB_EXT = ".jar";
private static String LAUNCHER_CLASS = "demo.launcher.AppLauncher";
private static ClassLoader cl;
static{
try {
cl = getClassLoaderFromPath(
new File(CLASSPATH_DIR),
Thread.currentThread().getContextClassLoader()
);
Thread.currentThread().setContextClassLoader(cl);
} catch (Exception e) {
e.printStackTrace();
}
}
// Returns a ClassLoader that for the provided path.
private static ClassLoader getClassLoaderFromPath(File path, ClassLoader parent) throws Exception {
// get jar files from jarPath
File[] jarFiles = path.listFiles(new FileFilter() {
public boolean accept(File file) {
return file.getName().endsWith(Main.LIB_EXT);
}
});
URL[] classpath = new URL[jarFiles.length];
for (int j = 0; j < jarFiles.length; j++) {
classpath[j] = jarFiles[j].toURI().toURL();
}
return new URLClassLoader(classpath, parent);
}
public static void main(String[] args) throws Exception{
Launcher launcher = Launcher.class.cast(
Class.forName(LAUNCHER_CLASS, true, cl).newInstance()
);
launcher.launch(new Object[]{"this string is capitalized"});
}
}
Listing-2
The first thing to notice is the static declarations at the start of the listing. The first three declarations setups the “lib” directory as the location for the classpath, provides “.jar” as the file extension, and specifies demo.launcher.AppLauncher as the name of the Launcher class to load from the classpath. The static code block uses method getClassLoaderFromPath() to initialize a URLClassLoader instance (that points to the lib directory) that will serve as the class loader for the rest of the application.
When the public static void main() method in the Main class is invoked (by the Java launcher), it searches and loads an instance of class demo.launcher.AppLauncher which implements Launcher. Then, the code calls Launcher.launch() to delegate the execution of the rest of the application by passing in a String parameter.
The Launcher Class
The Launcher class is responsible for starting up the application-specific logic. Implementation of the launch() method maintains the same signature as the the PSVM method from the Main class to maintain the familiar semantic. Parameters are passed in as arrays of objects and the method is expected to return an integer. A return value of 0 means everything is OK while anything else means something up to the discretion of the implementor. Listing-3 shows a simple implementation of the Launcher class.
public class AppLauncher implements Launcher {
public int launch(Object ... args) {
String result = org.apache.commons.lang3.text.WordUtils.capitalize((String)args[0]);
System.out.println (result);
return 0;
}
}
Listing-3
How It Works
This implementation uses Apache Commons-Lang to capitalize the value of an argument that was passed in. While this is a simple example, it shows exactly how the pattern would work. When the application is invoked from the command-line using
$ java -jar demo.launcher.Main
The Main class resolves the classpath by loading jars from the jar directory. The classpath directory contains all jars that satisfies the dependency graph of the application. In this example the application depends on the Apache-Commons Lang jar. When Main instantiates its ClassLoader instance, the jar will be added on the classpath and thus be available for use.
An Example
You can download example code that shows how this works from the location below:
An example – https://github.com/vladimirvivien/workbench/tree/master/CustomLauncher
The example comes in three separate projects:
- Launcher-Api – contains the definition of the Launcher interface.
- Launcher-Impl – contains an implementation of the Launcher interface.
- Laucnher-Main – contains the Main class that is used as the starting point of the application.
Conclusion
The Loader/Launcher Pattern is an attempt to decouple two distinct activities that occur when a Java application is started: that of class loading and and application start up. The pattern uses a Main class as the entry point from the Java native launcher and is used to load the application’s classpath from a given location. The act of activating the application is then relegated to a Launcher class. The launcher is responsible for actually starting up the application-specific logic in the code. Some of the benefit of adopting this pattern is, firstly, the tighter control over how classes are loaded. You no longer have to rely on the native Java launcher to resolve your classpath. Another benefit is the separation of concerns for the start up sequence of the app. The pattern provides a location, the Launcher interface, where to define what should happen when the application itself (not loading of classpath) is starting. Hope this was helpful.
Reference
https://github.com/vladimirvivien/workbench/tree/master/CustomLauncher - the example
http://code.google.com/p/clamshell-cli/ - tool that uses this pattern
