In Search of Quotes

February 14, 2013 2 comments

Wikiquote

My search continues for a source of interesting quotations that I can incorporate in my mobile application. Wikimedia’s Wikiquote appeared to be an excellent source. It holds thousands of quotes. Wikiquote describes itself as a free compendium of quotations that is being written collaboratively by the readers.  The trick is how to access those quotes with an API.

Wikiquote is powered by MediaWiki the software that runs various Wikimedia sites such as Wikipedia. MediaWiki provides a web service API to access its pages. But while the page contents is easily accessed it is not structured in way that allows it to be easily parsed into discrete data elements. In other words, extract quotes from any given Wikiquote page isn’t straight forward. I could write a parser but I suspected that something like this had already been done. This suspicion lead me to  DBpedia.

DBpediaDBpedia describes itself as …

… a crowd-sourced community effort to extract structured information from Wikipedia and to make this information available on the Web. DBpedia allows you to make sophisticated queries against Wikipedia, and to link other data sets on the Web to Wikipedia data.

DBpedia regularly extracts data from Wikipedia and stores it using a Resource Description Framework (RDF) model for data interchange. Those resources can be remotely queried using SPARQL a query language to RDF.  DBpedia’s ontology contains a quotation property. Unfortunately when I started querying DBpedia for resources that had quotes very few were returned. Apparently Wikiquote is not one of the Wikimedia sites that DBpedia sources from. So while DBpedia looked promising it turned out to be a dead end.

Freebase

More searching lead me to Freebase. Freebase is very similar to DBpedia. It is an open collection of structured data that can be accessed using a remote API. Here too data is pulled from a variety of sources such as Wikipedia and stored as a  graph model comprised of nodes (data objects) and relationships between nodes. This model can be queried with Freebase’s proprietary Metaweb Query Language (MQL).

For example, the follow query will return quotations for Albert Einstein.

[{
  "type": "/people/person",
  "id": null,
  "name": "Albert Einstein",
  "gender": {
    "type": "/people/gender",
    "id": null,
    "name": null
    },
  "/people/person/quotations": [{
      "type": "/media_common/quotation",
      "id": null,
      "name": null,
      "subjects": [],
      "limit": 5
      }]
  }]

Here are the results …

<em id="__mceDel">{
  "code":          "/api/status/ok",
  "result": [{
    "/people/person/quotations": [
      {
        "id":       "/en/imagination_is_more_important_than_knowledge",
        "name":     "Imagination is more important than knowledge.",
        "subjects": [],
        "type":     "/media_common/quotation"
      },
      {
        "id":       "/m/02kpjn_",
        "name":     "Great spirits have always encountered violent opposition from mediocre minds.",
        "subjects": [],
        "type":     "/media_common/quotation"
      },
      {
        "id":       "/m/02nrfj2",
        "name":     "Not everything that counts can be counted, and not everything that can be counted counts.",
        "subjects": [],
        "type":     "/media_common/quotation"
      },
      {
        "id":   "/quotationsbook/quote/21171",
        "name": "If men as individuals surrender to the call of their elementary instincts, avoiding pain and seeking satisfaction only for their own selves, the result for them all taken together must be a state of insecurity, of fear, and of promiscuous misery.",
        "subjects": [
          "Instinct"
        ],
        "type": "/media_common/quotation"
      },
      {
        "id":   "/quotationsbook/quote/23603",
        "name": "The ideals which have always shone before me and filled me with the joy of living are goodness, beauty, and truth.",
        "subjects": [
          "Life and Living"
        ],
        "type": "/media_common/quotation"
      }
    ],
    "gender": {
      "id":   "/en/male",
      "name": "Male",
      "type": "/people/gender"
    },
    "id":   "/en/albert_einstein",
    "name": "Albert Einstein",
    "type": "/people/person"
  }],
  "status":        "200 OK",
  "transaction_id": "cache;cache02.p01.sjc1:8101;2013-02-14T06:45:22Z;0064"
}

With Freebase I now had an online source for thousands of interesting quotes. The next step was how best to use them.

Advertisements
Categories: Bruce's Posts, Java

Fortune Files

February 8, 2013 Leave a comment

Creating a quote of the day application requires quotes. So where does one go to get a lots of free quotes that are actually interesting. One place is a fortune file.

A fortune file contains a set of quotes similar to those found in a fortune cookie. Fortune files comprise the underlying database of the fortune program which appeared over 30 years with the release of Version 7 UNIX.

Since then creating and publishing fortune files has become a cottage industry. A quick search of the web will turn up numerous fortune files reflecting a wide variety genres. I download one from here. I contains over 9000 quotes. Next, how to incorporate it into my application?

The first step invokes preparing the fortune file for quick random lookup. A fortune file is a text file. Each line in the file contains a unique quote.

Rocks might teach us life's secrets, were it not for the language barrier.
Romance addiction is an invention of Western culture. — Anne Schaeff

The quote of the day widgets will random choose one of the quotes from the fortune file each day. This adds the following application requirements:

  • The widgets and activity need a common way to get the current day’s quote.
  • Each day the widgets need to be updated with new quotes.

An Android Service will provide an method to get quotes.

QuoteService extends IntentService. 

IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.

This “work queue processor” pattern is commonly used to offload tasks from an application’s main thread. The IntentService class exists to simplify this pattern and take care of the mechanics. To use it, extend IntentService and implement onHandleIntent(Intent). IntentService will receive the Intents, launch a worker thread, and stop the service as appropriate.

All requests are handled on a single worker thread — they may take as long as necessary (and will not block the application’s main loop), but only one request will be processed at a time.

The initial version of QuoteService reads the Fortune file that’s been packaged as a raw resource and caches the quotes in an array. Not a great final solution but sufficient for now.

QuoteService.java

public class QuoteService extends IntentService {
	static private String TAG = QuoteService.class.getSimpleName();

	private final IBinder binder = new QuoteBinder();

	public class QuoteBinder extends Binder {
		public QuoteService getService() {
            	return QuoteService.this;
		}
	}

	private final Random random = new Random();

	private String[] quotes = { "Never put off until tomorrow what you can do the day after tomorrow. -- Mark Twain" };

	public String[] getQuotes() {
		return quotes;
	}

	public void setQuotes(String[] quotes) {
		this.quotes = quotes;
	}

	public String getQuote(int index) {
		return quotes != null ? quotes[index] : null;
	}

	public String qetRandomQuote() {
		return getQuote(random.nextInt(quotes.length));
	}

	public QuoteService() {
		super(TAG);
		Log.d(TAG, String.format("QuoteService"));
	}

	@Override
	public IBinder onBind(Intent intent) {
		Log.d(TAG, String.format("onBind - intent: %s", intent));
		return binder;
	}

	@Override
	public void onCreate() {
		Log.d(TAG, String.format("onCreate"));

		InputStream inputStream = this.getResources().openRawResource(R.raw.fortunes);
		Reader reader = new InputStreamReader(inputStream);
		BufferedReader in = new BufferedReader(reader);

		List<String> list = new ArrayList<String>();
		try {
			String string;

			while ((string = in.readLine()) != null) {
				Log.d(TAG, String.format("string: %s", string));
				list.add(string);
			}
			in.close();
		} catch (IOException e) {
		}

		quotes = list.toArray(new String[list.size()]);
		Log.d(TAG, String.format("quotes read: %d", quotes.length));
	}

	@Override
	public void onDestroy() {
		Log.d(TAG, String.format("onDestroy"));
	}

	@Override
	protected void onHandleIntent(Intent intent) {
		Log.d(TAG, String.format("onHandleIntent - intent: %s", intent));
	}
}

When QuoteActivity starts it binds to QuoteService. When the asynchronous connection to the service is made QuoteActivity’s views are then be updated if currently empty. If QuoteActivity is started with an Intent that contains a quote then that quote will be display. This allows QuoteActivity to be launched from a widget. The widget’s quote is passed along. Right now QuoteActivity just reproduces the quote. Later it will display more more information related to that quote.

QuoteActivity.java

public class QuoteActivity extends Activity {
	static private String TAG = QuoteActivity.class.getSimpleName();
	static public String QUOTE = "org.bwgz.qotd.activity.QuoteActivity.QUOTE";

	private QuoteService service;
	private String quote;
	private boolean connected = false;

	public boolean isConnected() {
		return connected;
	}

	public void setConnected(boolean connected) {
		this.connected = connected;
	}

	public String getQuote() {
		return quote;
	}

	public QuoteService getService() {
		return service;
	}

	public void setService(QuoteService service) {
		this.service = service;
	}

	public void setQuote(String quote) {
		this.quote = quote;

    		TextView textView = (TextView)findViewById(R.id.quote);
    		textView.setText(quote);
	}

	private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className, IBinder binder) {
    		Log.d(TAG, String.format("onServiceConnected - className: %s  service: %s", className, binder));

            setConnected(true);
            setService(((QuoteBinder) binder).getService());

            if (getQuote() == null) {
            	setQuote(getService().qetRandomQuote());
            }

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
    		Log.d(TAG, String.format("onServiceDisconnected - name: %s", name));

            setConnected(false);
        }
	};

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.d(TAG, String.format("onCreate - savedInstanceState: %s", savedInstanceState));

        	setContentView(R.layout.activity_quote);
	}

    @Override
    protected void onStart() {
        super.onStart();
		Log.d(TAG, String.format("onStart"));

        bindService(new Intent(this, QuoteService.class), connection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onResume() {
	super.onResume();
	Log.d(TAG, String.format("onResume"));

	Intent intent = getIntent();
	String quote = intent.getStringExtra(QUOTE);
	Log.d(TAG, String.format("intent: %s  quote: %s", intent, quote));

	if (quote != null) {
		setQuote(quote);
	}
    }

    @Override
    protected void onPause() {
	super.onPause();
	Log.d(TAG, String.format("onPause"));
    }

    @Override
    protected void onStop() {
	super.onStop();
	Log.d(TAG, String.format("onStop"));

	if (isConnected()) {
            unbindService(connection);
            setConnected(false);
        }
    }

    @Override
    protected void onDestroy() {
	super.onDestroy();
	Log.d(TAG, String.format("onDestroy"));
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
	MenuInflater inflater = getMenuInflater();
	inflater.inflate(R.menu.options, menu);
	return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
	if (item.getItemId() == android.R.id.home || item.getItemId() == 0) {
            return false;
	}
	if (item.getItemId() == R.id.developer) {
    	    Intent intent = new Intent(this, DeveloperActivity.class);
    	    startActivity(intent);
    	}

	return true;
    }
}

Things aren’t were a little more complicated with QuoteWidgetProvider. Android does not allow a BroadcastReceiver to bind to a Service.

Because AppWidgetProvider is an extension of BroadcastReceiver, your process is not guaranteed to keep running after the callback methods return (see BroadcastReceiver for information about the broadcast lifecycle). If your App Widget setup process can take several seconds (perhaps while performing web requests) and you require that your process continues, consider starting a Service in the onUpdate()method. From within the Service, you can perform your own updates to the App Widget without worrying about the AppWidgetProvider closing down due to an Application Not Responding (ANR) error.

I created QuoteOfTheDayService to act as a proxy for QuoteWidgetProvider. QuoteOfTheDayService does a few things:

  • It binds to QuoteService so that it can access the quotes.
  • It ensures all the widgets have a random quote.
  • Using a custom Handler it refreshes all the widgets with new random quotes when the current day rolls over.

QuoteOfTheDayService.java

public class QuoteOfTheDayService extends IntentService {
	static private String TAG = QuoteOfTheDayService.class.getSimpleName();
	static public String APP_WIDGET_IDS = "org.bwgz.qotd.service.QuoteOfTheDayService.APP_WIDGET_IDS";

        private QuoteService quoteService;
	private boolean connected = false;

	public QuoteService getQuoteService() {
		return quoteService;
	}

	public void setQuoteService(QuoteService quoteService) {
		this.quoteService = quoteService;
	}

        public boolean isConnected() {
		return connected;
	}

	public void setConnected(boolean connected) {
		this.connected = connected;
	}

	static private class AlarmHandler extends Handler {
    	private QuoteOfTheDayService qotdService;

		public AlarmHandler(QuoteOfTheDayService service) {
    		this.qotdService = service;
    	}

        @Override
        public void handleMessage(Message msg) {
            ComponentName widget = new ComponentName(qotdService, QuoteWidgetProvider.class);
            AppWidgetManager manager = AppWidgetManager.getInstance(qotdService);

            qotdService.updateAppWidgets(manager.getAppWidgetIds(widget));
            qotdService.setAlarm();
        }
    }

    private AlarmHandler handler = new AlarmHandler(this);

	private long getTest() {
		Calendar calendar = GregorianCalendar.getInstance();
		int year = calendar.get(Calendar.YEAR);
		int month = calendar.get(Calendar.MONTH);
		int day = calendar.get(Calendar.DAY_OF_MONTH);
		int hour = calendar.get(Calendar.HOUR_OF_DAY);
		int minute = calendar.get(Calendar.MINUTE);
		int second = calendar.get(Calendar.SECOND);

		calendar = new GregorianCalendar(year, month, day, hour, minute + 1, second);
		Log.d(TAG, String.format("next: %s", calendar));

		return calendar.getTimeInMillis();
	}

	@SuppressWarnings("unused")
	private long getMidnight() {
		Calendar calendar = GregorianCalendar.getInstance();
		int year = calendar.get(Calendar.YEAR);
		int month = calendar.get(Calendar.MONTH);
		int day = calendar.get(Calendar.DAY_OF_MONTH);

		calendar = new GregorianCalendar(year, month, day);
		Log.d(TAG, String.format("today: %s", calendar));
		calendar.roll(Calendar.DATE, true);
		Log.d(TAG, String.format("tomorrow: %s", calendar));

		return calendar.getTimeInMillis();
	}

	public void setAlarm() {
		long next = getTest();
		long now = System.currentTimeMillis();
		long delta = next - now;
		Log.d(TAG, String.format(" next: %d", next));
		Log.d(TAG, String.format("  now: %d", now));
		Log.d(TAG, String.format("delta: %d", delta));

		handler.sendMessageDelayed(handler.obtainMessage(), delta);
	}

	private ServiceConnection connection = new ServiceConnection() {
		@Override
		public void onServiceConnected(ComponentName name, IBinder binder) {
    		Log.d(TAG, String.format("onServiceConnected - name: %s  binder: %s", name, binder));

    		setConnected(true);

    		setQuoteService(((QuoteBinder) binder).getService());
    		Log.d(TAG, String.format("service: %s", getQuoteService()));

                ComponentName widget = new ComponentName(QuoteOfTheDayService.this, QuoteWidgetProvider.class);
                AppWidgetManager manager = AppWidgetManager.getInstance(QuoteOfTheDayService.this);

    		updateAppWidgets(manager.getAppWidgetIds(widget));
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
    		Log.d(TAG, String.format("onServiceDisconnected - name: %s", name));

    		setConnected(false);
		}
    };

	public QuoteOfTheDayService() {
		super(TAG);
	}

	private void updateAppWidgets(int[] appWidgetIds) {
		Log.d(TAG, String.format("updateWidgets - appWidgetIds: %s", appWidgetIds));

		if (appWidgetIds!= null) {
	        for (int appWidgetId : appWidgetIds) {
	    		Log.d(TAG, String.format("widgetId: %s", appWidgetId));
	    		String quote = getQuoteService() != null ? getQuoteService().qetRandomQuote() : new String();

	        	RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.widget_quote);
	        	remoteViews.setTextViewText(R.id.quote, quote);

	        	Intent intent = new Intent(this, QuoteActivity.class);
	        	intent.putExtra(QuoteActivity.QUOTE, quote);
	                PendingIntent pendingIntent = PendingIntent.getActivity(this, appWidgetId, intent, PendingIntent.FLAG_CANCEL_CURRENT);

	        	remoteViews.setOnClickPendingIntent(R.id.widget, pendingIntent);

	                AppWidgetManager manager = AppWidgetManager.getInstance(QuoteOfTheDayService.this);
	        	manager.updateAppWidget(appWidgetId, remoteViews);
	        }
		}
	}

	@Override
	public void onCreate() {
		Log.d(TAG, String.format("onCreate"));

		bindService(new Intent(this, QuoteService.class), connection, Context.BIND_AUTO_CREATE);
		setAlarm();
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.d(TAG, String.format("onStartCommand - intent: %s  flags: %d  startId: %d", intent, flags, startId));

		int[] appWidgetIds = intent.getIntArrayExtra(APP_WIDGET_IDS);
		updateAppWidgets(appWidgetIds);

		return Service.START_NOT_STICKY;
	}

	@Override
	public void onDestroy() {
            Log.d(TAG, String.format("onDestroy"));

            if (isConnected()) {
                unbindService(connection);
                setConnected(false);
            }
	}

	@Override
	protected void onHandleIntent(Intent intent) {
	}

}

There were a few tricky bits. One was getting each widget to pass along its quote when clicked. I keep getting all the widgets pass the quote of the last widget in the list. Even though all the widgets has a different quote they same quote was passed with the Intent. The important parts was …

PendingIntent pendingIntent = PendingIntent.getActivity(this, appWidgetId, intent, PendingIntent.FLAG_CANCEL_CURRENT);

The documentation states …

public static PendingIntent getActivity (Context context, int requestCode, Intent intent, int flags, Bundle options)

requestCode Private request code for the sender (currently not used).

In fact you can specify the widget id in the requestCode parameter.

When QuoteWidgetProvider starts QuoteOfTheDayService  it passes the id’s of the widgets needing updating.  Here’s what it looks link now.

QuoteWidgetProvider.java

 public class QuoteWidgetProvider extends AppWidgetProvider {
     static private String TAG = QuoteWidgetProvider.class.getSimpleName();

     @Override
     public void onEnabled(Context context) {
         Log.d(TAG, String.format("onEnabled  - context %s", context));
     }

     @Override
     public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
         Log.d(TAG, String.format("onUpdate - context %s  appWidgetManager: %s  appWidgetIds: %s", context, appWidgetManager, appWidgetIds));

Intent intent = new Intent(context, QuoteOfTheDayService.class);
         intent.putExtra(QuoteOfTheDayService.APP_WIDGET_IDS, appWidgetIds);
         context.startService(intent);
     }

     @Override
     public void onDeleted(Context context, int[] appWidgetIds) {
         Log.d(TAG, String.format("onDeleted - context %s  appWidgetIds: %s", context, appWidgetIds));
     }

     @Override
     public void onDisabled (Context context) {
         Log.d(TAG, String.format("onDisabled   - context %s", context));

         context.stopService(new Intent(context, QuoteOfTheDayService.class));
     }
 }
 

The code is available at GitHub.

Categories: Android, Bruce's Posts, Java

What I’m I Working With

February 6, 2013 Leave a comment

Supporting a variety of Android devices can be challenging. To begin with there are currently 17 versions of Android. Then there’s the wide variety of screen sizes and densities. When developing an Android application it would be nice to quickly and easily understand the device’s properties.  I decided to created a simple library which displayed the following property sets:

  •  Display – General information about a display, such as its size, density, and font scaling.
  • OS – Information about the current build, extracted from system properties.
  • System – System related information.

With this library in place my application can invoke it from a menu option that is available while I develop it.

I created an Fragment sub-class to be used within the various Activities. The fragment contains a ListView and requires a ListAdapter and List from its sub-classes.

SimpleListViewFragment.java

public abstract class SimpleListViewFragment extends Fragment {
    abstract protected ListAdapter getAdapter(Context context);

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_simplelistview, container, false);

        ListView screenPropertiesView = (ListView) view.findViewById(R.id.listView);
        screenPropertiesView.setAdapter(getAdapter(container.getContext()));

        return view;
    }
}

fragment_simplelistview.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </ListView>

</LinearLayout>

Next is an other abstract class SimpleTwoLineListViewFragment which provides the adapter and requires the list from the sub-class.

SimpleTwoLineListViewFragment.java

public abstract class SimpleTwoLineListViewFragment extends SimpleListViewFragment {
	abstract protected List getList();

	protected ArrayAdapter getAdapter(Context context) {
		return new ArrayAdapter(context, android.R.layout.simple_list_item_2, getList()){
	        @Override
	        public View getView(int position, View convertView, ViewGroup parent){
	            TwoLineListItem view;
	            if(convertView == null){
	                LayoutInflater inflater = (LayoutInflater)parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	                view = (TwoLineListItem)inflater.inflate(android.R.layout.simple_list_item_2, null);
	            }else{
	                view = (TwoLineListItem)convertView;
	            }
	            T data = getItem(position);
	            view.getText1().setText(data.getName());
	            view.getText2().setText(data.getValue());

	            return view;
	        }
	    };
	}
}

Finally, there are the three classes DisplayPropertiesFragmentOSPropertiesFragment, and SystemPropertiesFragment which provide the properties in a List the properties.

DisplayPropertiesFragment.java

public class DisplayPropertiesFragment extends SimpleTwoLineListViewFragment {
	private String getDensityString(int density) {
		String string;

		if (density <= DisplayMetrics.DENSITY_LOW) {
			string = "low";
		}
		else if (density <= DisplayMetrics.DENSITY_MEDIUM) {
			string = "medium";
		}
		else if (density <= DisplayMetrics.DENSITY_TV) {
			string = "tv";
		}
		else if (density <= DisplayMetrics.DENSITY_HIGH) {
			string = "high";
		}
		else {
			string = "xhigh";
		}

		return string;
	}

	@Override
	protected List getList() {
		List list = new ArrayList();

		DisplayMetrics metrics = getResources().getDisplayMetrics();

		list.add(new TwoLineData("Width (pixels)", Integer.toString(metrics.widthPixels)));
		list.add(new TwoLineData("Height (pixels)", Integer.toString(metrics.heightPixels)));
		list.add(new TwoLineData("Density", Double.toString(metrics.density)));
		list.add(new TwoLineData("Density DPI", String.format("%d (%s)", metrics.densityDpi, getDensityString(metrics.densityDpi))));
		list.add(new TwoLineData("Scaled Density", Double.toString(metrics.scaledDensity)));
		list.add(new TwoLineData("xdpi", Double.toString(metrics.xdpi)));
		list.add(new TwoLineData("ydpi", Double.toString(metrics.ydpi)));

		return list;
	}

Android introduced fragments in Android 3.0 (API level 11) . I’ve got a level 10 device and what my application to support that.  Fortunately Android provides a Support Library which will add API’s not available for older platform versions.

To use these fragments I created DeveloperFragmentActivity. This class is designed to be called by an Intent with an extra string defining the name of the fragment class to instantiate and embedded (via Fragment.replace()).

DeveloperFragmentActivity.java

public class DeveloperFragmentActivity extends FragmentActivity {

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_developer);

        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
    	String fragmentClass = this.getIntent().getStringExtra("fragment");

        try {
			Fragment fragment = (Fragment) Class.forName(fragmentClass).newInstance();

	        fragmentTransaction.replace(R.id.mainFragement, fragment);
	        fragmentTransaction.commit();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
    }
}

DeveloperActivity and DeveloperPropertiesFragment bring it all together.

DeveloperActivity.java

public class DeveloperActivity extends FragmentActivity {

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_developer);

    FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();

    Fragment fragment = new DeveloperPropertiesFragment();
    fragmentTransaction.replace(R.id.mainFragement, fragment);
    fragmentTransaction.commit();
    }
}

DeveloperPropertiesFragment.java

class ListData {
private String title;
private String description;
private String fragment;

public ListData(String title, String description, String fragment) {
this.setTitle(title);
this.setDescription(description);
this.setFragment(fragment);
}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getFragment() {
		return fragment;
	}

	public void setFragment(String fragment) {
		this.fragment = fragment;
	}
}

public class DeveloperPropertiesFragment extends SimpleListViewFragment<ListData> {
	private List<ListData> list;

	private List<ListData> getList() {
		return list;
	}

	@Override
public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		list = new ArrayList<ListData>();
		list.add(new ListData(getString(R.string.display_item_title), getString(R.string.display_item_description), DisplayPropertiesFragment.class.getName()));
		list.add(new ListData(getString(R.string.os_item_title), getString(R.string.os_item_description), OSPropertiesFragment.class.getName()));
		list.add(new ListData(getString(R.string.system_item_title), getString(R.string.system_item_description), SystemPropertiesFragment.class.getName()));
	}

	@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		View view = super.onCreateView(inflater, container, savedInstanceState);

		ListView listView = (ListView) view.findViewById(R.id.listView);
		listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

		listView.setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
			    Intent intent = new Intent(view.getContext(), DeveloperFragmentActivity.class);
			    intent.putExtra("fragment", getList().get(position).getFragment());
			    startActivity(intent);
			}
		});

		return view;
	}

	protected ArrayAdapter<ListData> getAdapter(Context context) {
		return new ArrayAdapter<ListData>(context, android.R.layout.simple_list_item_2, getList()){
	        @Override
	        public View getView(int position, View convertView, ViewGroup parent){
	        	TwoLineListItem view;

	            if(convertView == null){
	                LayoutInflater inflater = (LayoutInflater)parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	                view = (TwoLineListItem)inflater.inflate(android.R.layout.simple_list_item_2, null);
	            }else{
	                view = (TwoLineListItem)convertView;
	            }

	            ListData data = getItem(position);
	            view.getText1().setText(data.getTitle());
	            view.getText2().setText(data.getDescription());

	            return view;
	        }
	    };
	}
}

The result is a set of ListView’s that display a variety of device properties.

developer sample

The code is available at GitHub.

Categories: Android, Java

You can quote me on that.

January 31, 2013 Leave a comment

quotation_iconI’m developing a mobile application that displays a famous quote and allows the user to share it. I’ll use an iterative approach that builds up the application over time; my personal version of Scrum. Start out with a simple application with a  limited set of canned quotes and grow out from there. 

I’m starting out with Android. Android open source development is much easier and cheaper than iOS or a Windows phone. The biggest issue I find with Android is version support. Android has gone through significant changes over the last few years. For example the popular ActionBar was added to Android 3.0 (API level 11)  two years ago.  I’d like my application to support Android 2.3.3 Gingerbread (API level 10) and above. I found that widget settings on an Android 2.3.7 device didn’t work the same as on an Android 4.2 emulated device. Picking the version sweat spot is something that you always need to keep you eye on.

The first version will be an Android application with a simple view and associated widget. My first version includes only the very basics:

  • An Activity with a TextView.
  • A widget with a TextView and ImageView.
  • A static string containing a quote.

Here are couple of screen shots from an Nexus emulation.

This home screen contains the Quote of the Day widget.

Quote of the Day application.

Samsung Developer has a nice emulation application available from their Remote Test Lab. I comes up much faster than AVD and comes with the skin. The only downside I’ve found so far is that it will only run for a predefined period of time.

The code is available at GitHub.

Categories: Android, Bruce's Posts, Java, Mobile

REST Service Using Spring Data

October 24, 2012 1 comment

I was intrigued by Spring Data’s REST project which promises to, “… make it easy to expose JPA based repositories as RESTful endpoints.” To try it out I decided to create a service which provides famous quotes such as Albert Einstein’s.

Insanity: doing the same thing over and over again and expecting different results.

Consider a JPA entity that holds a quote.

@Entity
@Table(name = "quote")
public class Quote {
	@Id
	@GeneratedValue
	private Long id;
	private String quote;

	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}

	public String getQuote() {
		return quote;
	}
	public void setQuote(String quote) {
		this.quote = quote;
	}

}

This is a very simple example of an entity. Just a id and text string.

Now create a repository service to expose CRUD style access to the entity. For now I’m just going to read a quote so all that’s needed is the following interface.

@RestResource(path = "quote")
public interface QuoteRepository extends PagingAndSortingRepository<Quote, Long> {
}

Spring Data provide a paging repository (PagingAndSortingRepository) and the simpler CrudRepository.

All the magic is in the @RestResource annotation. When the Spring REST Exporter starts up it will expose the QuoteRespository as a REST endpoint.

The Spring REST Exporter is a web application build on top of Spring MVC. The documentation is sparse but examples are pretty straight forward.

Also the Spring REST Exporter example uses gradle. I want to use a combination of Maven and Eclipse. It uses Java classes to configure the application environment. I wanted to use XML application context files. I also wanted multiple projects to encapsulate various applications and libraries. The entire source code is available here at github. Getting all the pieces just right took some time. I ran the quote war using Tomcat 7.

My quote endpoint will expose a list of quotes. In this case I’ve exposed paging and sorting possibilities.

http://localhost:8080/quote-json-server/quote produces a list of quotes stored in the repository.

{
  "links" : [ ],
  "content" : [ {
    "links" : [ {
      "rel" : "self",
      "href" : "http://localhost:8080/quote-json-server/quote/1"
    } ],
    "quote" : "Insanity: doing the same thing over and over again and expecting different results."
  }, {
    "links" : [ {
      "rel" : "self",
      "href" : "http://localhost:8080/quote-json-server/quote/2"
    } ],
    "quote" : "Better to remain silent and be thought a fool than to speak out and remove all doubt."
  } ],
  "page" : {
    "size" : 20,
    "totalElements" : 2,
    "totalPages" : 1,
    "number" : 1
  }
}

The output is a JSON serialization of the org.springframework.hateoas.PagedResources class. The serialization uses a HATEOAS (Hypermedia as the Engine of Application State) style format to provide a state representation of the resource.

In this case it is the state of the first page of the quotes list.

http://localhost:8080/quote-json-server/quote/1 provides the state of the first quote.

{
  "links" : [ {
    "rel" : "self",
    "href" : "http://localhost:8080/quote-json-server/quote/1"
  } ],
  "quote" : "Insanity: doing the same thing over and over again and expecting different results."
}

Having come from RPC as the method of choice REST takes a bit of getting used to. Roy T. Fielding’s REST APIs must be hypertext-driven discusses this. The biggest thing being individual resources are identified in requests. In this case a href (URL).

The next step is to create a client application to test the service. I created two clients. One a simple Java client and the other a simple Android application. REST clients are free to deserialize the response in any way they wish. I chose to use Spring’s RestTemplate API. Here’s the simple Java Client.

public class QuoteRestTemplateClient {
  private static final String URL = "http://localhost:8080/quote-json-server/quote/1";

  public static void main(String[] args) {
   RestTemplate restTemplate = new RestTemplate();

  HttpHeaders requestHeaders = new HttpHeaders();
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.APPLICATION_JSON);
  requestHeaders.setAccept(acceptableMediaTypes);
  HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
  restTemplate.getMessageConverters().add(new MappingJacksonHttpMessageConverter());

  System.out.printf("url: %s\n", URL);
  try {
            ResponseEntity<Quote> entity = restTemplate.exchange(URL, HttpMethod.GET,              requestEntity, Quote.class);
            System.out.printf("json: %s\n", entity.getBody().toString());

            Quote quote = restTemplate.getForObject(URL, Quote.class);
            System.out.printf("quote: %s\n", quote.getQuote());
      } catch (RestClientException e) {
            e.printStackTrace();
    }
  }
}

The biggest challenge was creating the client side Quote class. There were actually a couple of challenges.

The Spring REST Explorer uses Spring’s HATEOAS API to wrap the JPA entity (entities in the case of a list). The response is either an instance of Resource or Resources. While I could have included the Spring’s HATEOAS jar with my application that would pulled in baggage that I wanted to avoid and as I found out later would also be problematic with the Android client. So I punted and just copied the six source files I needed into the project and edited them accordingly.

It would have been nice to have just used the service side Quote class with the client. This too was problematic because it’s annotated with JPA. This again would pull in jars I don’t want or need on the client side. Also, its getId() clashes with one found in the HATEOAS Identifier’s getId(). Here’s the client side Quote class.

public class Quote extends ResourceSupport {
	private String quote;

	public String getQuote() {
		return quote;
	}
	public void setQuote(String quote) {
		this.quote = quote;
	}
}

Pretty simple but then the example is simple. I’d truly want a way to easily generate the server and client side versions of Quote in a repeatable manner. Something to investigate on another day.

The Android application, if one discounts the necessary Android code, is identical to the Java client. It also needs the HATEOAS source files and the client side Quote class. Here’s the main Activity class.

public class MainActivity extends Activity {
    private static final String URL = "http://192.168.2.201:8080/quote-json-server/quote/1";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        TextView t1 =(TextView)findViewById(R.id.textView1);
        t1.setText(URL);

        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders requestHeaders = new HttpHeaders();
        List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
        acceptableMediaTypes.add(MediaType.APPLICATION_JSON);
        requestHeaders.setAccept(acceptableMediaTypes);
        HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
        restTemplate.getMessageConverters().add(new MappingJacksonHttpMessageConverter());

        String string;

        try {
        	ResponseEntity<Quote> entity = restTemplate.exchange(URL, HttpMethod.GET, requestEntity, Quote.class);
            string = entity.getBody().toString();
        } catch (RestClientException e) {
        	string = e.getMessage();
        }

        TextView t2 = (TextView)findViewById(R.id.textView2);
        t2.setText(string);

        Quote quote = restTemplate.getForObject(URL, Quote.class);
        TextView t3 =(TextView)findViewById(R.id.textView3);
        t3.setText(quote.getQuote());
    }
}

Here’s what it looks like.

 

Categories: Bruce's Posts, JPA, Spring Data

Console Based UI Development on BeagleBone

April 21, 2012 1 comment

When experimenting with various devices on the BeagleBone I often get to a point with my test programs where streaming output messages (i.e. printf) to a console is no long feasible. What’s really needed at that point is a UI that displays current state information such as the value of a GPIO pin. Depending on which OS the BeagleBone is running there are a variety of graphic user interfaces (GUI) available for use. But GUI’s are never simple and with the BeagleBone you either needs to invested in a display or use X/Windows to display the GUI on another personal computer. Another alternative is to use a console based UI.

The Angstrom release of Linux that ships with the BeagleBone includes the curses and it’s newer incarnation ncurses. If you’re familiar with C programming getting a ncurses application up and running will only take a few minutes. Here is a very simple ncurses program.

poll.c

#include <ncurses.h>

void main() {
initscr();
mvprintw(5, 3, "Hello world!");
getch();
endwin();
}

Compile it with …

gcc poll.c -lncurses -o poll

Executing poll will display “Hello world!” starting at Y, X position 5, 3 on the console and then wait for a key hit before exiting.

The next step is to monitor something. The BeagleBone pulses one of its LED’s like a heart beat. poll has been adapted to monitor if the brightness value is on (255) or off (0) and count the number of times it catches it on.

poll.c

#include <ncurses.h>
#include <stdio.h>
#include <stdlib.h>

void monitor(FILE *fp) {
        int run = 1;
        int count = 0;

        nodelay(stdscr, true);
        noecho();

        mvprintw(1, 1, "brightness:");
        mvprintw(2, 1, "     count:");

        while (run) {
                char buffer[16];
                memset(buffer, 0, sizeof(buffer));

                fseek(fp, 0, 0);
                fread(buffer, sizeof(char), sizeof(buffer), fp);

                int value = atoi(buffer);
                if (value != 0) {
                        count++;
                }

                mvprintw(1, 12, "%-3s", (value != 0 ? "on" : "off"));
                mvprintw(2, 12, "%d", count);

                int c = getch();
                switch(c) {
                        case 'q':
                                run = 0;
                                break;
                }
        }
}

void main() {
        char *file = "/sys/class/leds/beaglebone::usr0/brightness";
        FILE *fp;
        if ((fp = fopen(file, "r")) == NULL) {
                fprintf(stderr, "error: cannont open %s\n", file);
        }
        else {
                initscr();
                monitor(fp);
                endwin();

                fclose(fp);
        }
}

The program opens the sys-fs file that will provide the brightness value. After initializing ncurses it goes into a loop that reads the brightness value, increments the count if the LED is on, outputs the data, and checks if the user has hit the quit (‘q’) key.

The call to nodelay() prevents getch() from blocking and noecho() configures ncurses not to echo key hits.

Running poll.

When running poll I noticed that it seems to stagger at times. The application will noticeably pause on occasion. Since the application has to share clock cycles with the other applications it only gets a slice of CPU time. The size of the slice will vary over time and depends on what other things the CPU is being asked to execute. Linux provides the nice command which specifies the application’s priority on the kernel’s run queue. Running poll with a nice of -20 (the maximum) significantly improved its responsiveness.

As a next step I’ve been exploring how to use ncurses to create an application that treats input from a device (i.e. push button) wired to a BeagleBone expansion pin as UI input. For example, pressing a push button would be like pressing a key on the keyboard. More on that in the future.

 

 

Categories: BeagleBone

Open Lane Changes

April 21, 2012 3 comments

My Open Lane application has undergone some significant changes. I’ve added:

  1. Entity classes for a swim meet and events within the meet.
  2. Enumerators for Gender and Stroke.
  3. An AgeGroup class to encapsulate an event’s age category.
  4. JSF DataModel subclasses for Meet and Event along with a refactored SignUp backing bean.
  5. JPA services for all data input and output.
  6. JSF Converter classes for Gender, Stroke, and AgeGroup.
  7. CSV file initialization functions to populate the Swimmer, Meet, Event, and User tables.

Gender and stroke each represent a fixed set of constants which can be objectified in Java as an enumerator. An enumerator provides a clean, self-documenting, and an efficient way of coding an object model. Java enumerators can also be used to encapsulated conversion functionality.

In the case of this application the meet, event, swimmer, and entry records are imported from a CSV file. Within the event record is a stroke (i.e. Freestyle, Backstroke) filed that is coded using a number.  A “1” represents Freestyle, a “2” represents Backstroke, and so on. Stroke has been coded so that each enumerator has its import field number code associated with it. During the import process a simple call to Stroke.parse(string) generates the correct enumerator.

Instead of putting parse into Stroke I could have created a distinct converter. That would have been a cleaner separation of the code. But I don’t anticipate the conversion changing or needing to be adaptable. This way the code is simpler and straight forward.

Stroke.java

package org.bwgz.swim.openlane.data.model;

public enum Stroke {
    FREE("1"),
    BACK("2"),
    BREAST("3"),
    FLY("4"),
    IM("5");

    private String code;

    Stroke(String code) {
    	this.code = code;
    }

     public String getCode() {
          return code;
     }

     public void setCode(String code) {
          this.code = code;
     }

    public static Stroke parse(String string) {
    	Stroke result = null;

    	for (Stroke stroke : Stroke.values() ) {
    		if (stroke.getCode().equalsIgnoreCase(string)) {
				result = stroke;
				break;
			}
		}

		return result;
    }
}

This is an example of using the Gender enumerator within a JPA query to abstract away the codes used in the database. I don’t have to concern myself with how the data is coded once it is imported into the database.

em.createQuery("select e from Event e where e.meet = :meet and (e.gender = :mixed or e.gender = :gender")
.setParameter("meet", meet)
.setParameter("mixed", Gender.MIXED)
.setParameter("gender", gender)
.getResultList();
 

Later in this article is an example of how the enumerator’s parse method is used.

Eventually the JSF tables will support sorting and paging. In preparation for that I’ve moved from List and Collection to JSF’s DataModel. DataModel is a wrapper that abstracts the underlying data. I’ve also used it to store which record (object) may have been selected by the user. Previously there was an independent bean to handle that. Putting it here keeps thing a bit tidier. I created DataModel’s for Event and Meet. Later I’ll reuse the pattern for a user’s open lane applications.

I’ve decided to build out services for each entity in the data model. This provides a level of encapsulation and organization that make the code more manageable. Using @Autowired I can also create services that are built upon other services. For example, the SignUpService uses the SwimmerService.

SignUpService.java snippet.

@Service("signupService")
@Repository
public class SignUpServiceImpl implements SignUpService, Serializable {

     private EntityManager em;
     @Autowired
     private SwimmerService swimmerService;

    @PersistenceContext
    public void setEntityManager(EntityManager em) {
    	this.em = em;
    }
….
    @Transactional
     public Boolean doSignUp(SignUp signUp) {
     Boolean result = Boolean.FALSE;

     Swimmer swimmer = swimmerService.findSwimmer(signUp.getUsasId());

As a general rule the code should have a clean separation between how data is represented internally and externally. JSF’s Converter class is one way to accomplish that. I needed a Converter for Gender, Stroke, and AgeGroup. Since Gender and Stroke were enumerators I create an abstract base class to simplify things. The Gender and Stroke converters need only supply the mappings between the internal representation (an enumerator) and string (display value).

AbstractEnumConverter.java

package org.bwgz.swim.openlane.faces.converter;

import java.util.Hashtable;
import java.util.Map;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;

public abstract class AbstractEnumConverter<T> implements Converter {
	private final static int ASSOCIATION_ENUM	= 0;
	private final static int ASSOCIATION_STRING	= 1;

	private Class<T> clazz;
	private final Map<T, String> toStringMap = new Hashtable<T, String>();
	private final Map<String, T> toEnumMap = new Hashtable<String, T>();

	@SuppressWarnings("unchecked")
	public AbstractEnumConverter(Class<T> clazz, Object[][] associations) {
		this.clazz = clazz;

		for (Object[] association : associations) {
			toEnumMap.put((String) association[ASSOCIATION_STRING], (T) association[ASSOCIATION_ENUM]);
			toStringMap.put((T) association[ASSOCIATION_ENUM], (String) association[ASSOCIATION_STRING]);
		}
	}

	public Object getAsObject(FacesContext context, UIComponent component, String value) {
		return toEnumMap.get(value);
	}

	@SuppressWarnings("unchecked")
	public String getAsString(FacesContext context, UIComponent component, Object value) {
            if (value.getClass() == clazz) {
        	 return toStringMap.get((T) value);
            }
            else
            {
                throw new IllegalArgumentException(String.format("Cannot convert object - not of type %s", clazz.getSimpleName()));
            }
	}
}

StrokeConverter.java

package org.bwgz.swim.openlane.faces.converter;

import javax.faces.convert.FacesConverter;
import org.bwgz.swim.openlane.data.model.Stroke;

@FacesConverter(value="strokeConverter")
public class StrokeConverter extends AbstractEnumConverter<Stroke> {
	private final static Object associations[][] = {
		{ Stroke.FREE,		"Free" },
		{ Stroke.BACK,		"Back" },
		{ Stroke.BREAST,	"Breast" },
		{ Stroke.FLY,		"Fly" },
		{ Stroke.IM,		"IM" },
	};

	public StrokeConverter() {
		super(Stroke.class, associations);
	}
}

The AgeGroup converter encapsulates the four rules used to describe an age category.

AgeGroupConverter.java

package org.bwgz.swim.openlane.faces.converter;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;

import org.bwgz.swim.openlane.data.model.AgeGroup;

@FacesConverter(value="ageGroupConverter")
public class AgeGroupConverter implements Converter {
	private static final String SENIOR		= "Senior";
	private static final String UNDER		= "Under";
	private static final String OVER		= "Over";
	private static final String AMPERSAND	= "&";
	private static final String HYPHEN		= "-";

	private static final int LEFT	= 0;
	private static final int RIGHT	= 1;

	private Object getAsObject(String string) {
		long min = 0;
		long max = 0;

		if (string.equals(SENIOR)) {
			min = 0;
			max = 0;
		}
		else if (string.contains(AMPERSAND)) {
			String[] fields = string.split(AMPERSAND);

			if (fields[RIGHT].equals(UNDER)) {
				min = 0;
				max = Long.parseLong(fields[LEFT]);
			}
			else if (fields[RIGHT].equals(OVER)) {
				min = Long.parseLong(fields[LEFT]);
				max = 0;
			}
		}
		else if (string.contains(HYPHEN)) {
			String[] fields = string.split(HYPHEN);

			min = Long.parseLong(fields[LEFT]);
			max = Long.parseLong(fields[RIGHT]);
		}

		return new AgeGroup(min, max);
	}

	public Object getAsObject(FacesContext context, UIComponent component, String value) {
		return getAsObject(value);
	}

	private String getAsString(AgeGroup ageGroup) {
		String string;

		if (ageGroup.getMin() == 0 & ageGroup.getMax() == 0) {
			string = SENIOR;
		}
		else {
			String left;
			String seperator;
			String right;

			if (ageGroup.getMin() == 0) {
				left = String.valueOf(ageGroup.getMax());
				seperator = AMPERSAND;
				right = UNDER;
			}
			else if (ageGroup.getMax() == 0) {
				left = String.valueOf(ageGroup.getMin());
				seperator = AMPERSAND;
				right = OVER;
			}
			else {
				left = String.valueOf(ageGroup.getMin());
				seperator = HYPHEN;
				right = String.valueOf(ageGroup.getMax());
			}

			string = left + seperator + right;
		}

		return string;
	}

	public String getAsString(FacesContext context, UIComponent component, Object value) {
            if (value instanceof AgeGroup) {
                return getAsString((AgeGroup) value);
            }
            else
            {
                throw new IllegalArgumentException("Cannot convert object - not of type AgeGroup");
            }
	}
}

Meet, event, swimmer, and later entry records are imported from another system. Unfortunately the limitations of that system prevent the application from accessing them directly. Instead the data is exported to CSV files and then imported into the application.

Eventually I’ve incorporate a form of file upload within the application to provide live data import. For now some dummy (test) CSV files are included in the application and imported when the application is first accessed. Some services now have an initialize method. When called the method will read a CSV file and write the data to the database using JPA.

Using <on-start> within home-flow.xml I trigger the initializations. This is a hack for testing purposes only.

home-flow.xml snippet

<on-start>
        <evaluate expression="swimmerService.initialize()" />
        <evaluate expression="meetService.initialize()" />
        <evaluate expression="signupService.initialize()" />
</on-start>

I use FlatPack to process the CSV files. It’s a nice CSV library that I’ve used many times in the past. I particularly like that it allows you to create a map of the column names. On the other hand the map requires names for every column. This can be a bit tedious when you only need the first few columns. It can also choke when the file contains records with differing number of columns.

SwimmerServiceImpl.java snippet.

    @Transactional
    public void initialize() {
    if (!initialized) {
        DataSet dataSet;

        Parser parser;
        parser = DefaultParserFactory.getInstance().newDelimitedParser(
            new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream("/test/data/athlete.pzmap.xml")), // xml column mapping
            new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream("/test/data/athlete.csv")),  // csv file to parse
            ';', // delimiter
             '"', // text qualfier
             false); // ignore the first record (may need to be done if first record contain column names)

            dataSet = parser.parse();
            while (dataSet.next()) {
                Swimmer swimmer = new Swimmer();

                swimmer.setId(dataSet.getString("Reg_ID"));
                swimmer.setFirst(dataSet.getString("First_name"));
                swimmer.setLast(dataSet.getString("Last_name"));
                swimmer.setGender(Gender.parse(dataSet.getString("Ath_Sex")));
                swimmer.setId(dataSet.getString("Reg_ID"));
                swimmer.setBirthdate(stringToDate(dataSet.getString("Birth_date")));

                em.persist(swimmer);
                }

            initialized = true;
        }
     }
 

The code is available here on GitHub.