<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Adrian Smith&#039;s Blog &#187; java</title>
	<atom:link href="http://www.17od.com/tag/java/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.17od.com</link>
	<description></description>
	<lastBuildDate>Sun, 20 Nov 2011 20:04:50 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
<xhtml:meta xmlns:xhtml="http://www.w3.org/1999/xhtml" name="robots" content="noindex" />
		<item>
		<title>Multipart form upload on Android</title>
		<link>http://www.17od.com/2010/02/18/multipart-form-upload-on-android/</link>
		<comments>http://www.17od.com/2010/02/18/multipart-form-upload-on-android/#comments</comments>
		<pubDate>Thu, 18 Feb 2010 19:49:55 +0000</pubDate>
		<dc:creator>Adrian</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[java]]></category>

		<guid isPermaLink="false">http://www.17od.com/?p=280</guid>
		<description><![CDATA[Android 1.5 includes Apache HttpClient 4 for the purposes of making HTTP requests. Unfortunately HttpClient 4 (or the version included with Android anyway) doesn&#8217;t appear to support multipart form uploads. As it happens writing a solution from scratch (at the HTTP layer that is) is pretty straight forward. The main challenge is understanding how the [...]]]></description>
			<content:encoded><![CDATA[<p>Android 1.5 includes <a href="http://hc.apache.org/httpcomponents-client/index.html">Apache HttpClient 4</a> for the purposes of making HTTP requests. Unfortunately HttpClient 4 (or the version included with Android anyway) doesn&#8217;t appear to support multipart form uploads.</p>
<p>As it happens writing a solution from scratch (at the HTTP layer that is) is pretty straight forward. The main challenge is understanding how the POST request should be structured. Once you know that it&#8217;s simply a matter of putting all the pieces together.</p>
<p>The connection is managed using the <strong>java.net.HttpURLConnection</strong> class. There&#8217;s a few properties that need to set on the connection to ensure it can be written to and read from,</p>
<pre><code>HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);</code></pre>
<p>A special header informs the server that this will be a multipart form submission.</p>
<pre><code>conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=xxxxxxxxxx");</code></pre>
<p>The &#8220;boundry&#8221; can be any string. In this example it&#8217;s <code>xxxxxxxxxx</code>. It&#8217;s used in the body of the request to seperate each field being submitted.</p>
<p>Next comes the main body of the request. Each field being submitted follows the same pattern. For example, to submit a text file called &#8220;helloworld.txt&#8221; with the contents &#8220;Hello World&#8221; and using the boundry string <code>xxxxxxxxxx</code> here&#8217;s what the request would look like,</p>
<pre><code>--xxxxxxxxxx
Content-Disposition: form-data; name="filetoupload"; filename="helloworld.txt"
Content-Type: text/plain

Hello World
--xxxxxxxxxx--</code></pre>
<p>The final <code>--</code> is important. It tells the server it&#8217;s the end of the submission.</p>
<p>Here&#8217;s a full method that&#8217;s used to upload a file to a URL. It can optionally take a username and password which is used to perform BASIC authentication.</p>
<pre><code>    public static void put(String targetURL, File file, String username, String password) throws Exception {

        String BOUNDRY = "==================================";
        HttpURLConnection conn = null;

        try {

            // These strings are sent in the request body. They provide information about the file being uploaded
            String contentDisposition = "Content-Disposition: form-data; name=\"userfile\"; filename=\"" + file.getName() + "\"";
            String contentType = "Content-Type: application/octet-stream";

            // This is the standard format for a multipart request
            StringBuffer requestBody = new StringBuffer();
            requestBody.append("--");
            requestBody.append(BOUNDRY);
            requestBody.append('\n');
            requestBody.append(contentDisposition);
            requestBody.append('\n');
            requestBody.append(contentType);
            requestBody.append('\n');
            requestBody.append('\n');
            requestBody.append(new String(Util.getBytesFromFile(file)));
            requestBody.append("--");
            requestBody.append(BOUNDRY);
            requestBody.append("--");

            // Make a connect to the server
            URL url = new URL(targetURL);
            conn = (HttpURLConnection) url.openConnection();

            // Put the authentication details in the request
            if (username != null) {
                String usernamePassword = username + ":" + password;
                String encodedUsernamePassword = Base64.encodeBytes(usernamePassword.getBytes());
                conn.setRequestProperty ("Authorization", "Basic " + encodedUsernamePassword);
            }

            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDRY);

            // Send the body
            DataOutputStream dataOS = new DataOutputStream(conn.getOutputStream());
            dataOS.writeBytes(requestBody.toString());
            dataOS.flush();
            dataOS.close();

            // Ensure we got the HTTP 200 response code
            int responseCode = conn.getResponseCode();
            if (responseCode != 200) {
                throw new Exception(String.format("Received the response code %d from the URL %s", responseCode, url));
            }

            // Read the response
            InputStream is = conn.getInputStream();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] bytes = new byte[1024];
            int bytesRead;
            while((bytesRead = is.read(bytes)) != -1) {
                baos.write(bytes, 0, bytesRead);
            }
            byte[] bytesReceived = baos.toByteArray();
            baos.close();

            is.close();
            String response = new String(bytesReceived);

            // TODO: Do something here to handle the 'response' string

        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }

    }</code></pre>
<p>There&#8217;s nothing in this code particular to Android so it will work in any java application.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.17od.com/2010/02/18/multipart-form-upload-on-android/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>How to Create a Remote Paging Listview Using GWT-Ext</title>
		<link>http://www.17od.com/2008/07/24/how-to-create-a-remote-paging-listview-using-gwt-ext/</link>
		<comments>http://www.17od.com/2008/07/24/how-to-create-a-remote-paging-listview-using-gwt-ext/#comments</comments>
		<pubDate>Thu, 24 Jul 2008 18:05:36 +0000</pubDate>
		<dc:creator>Adrian</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[gwt]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://www.17od.com/?p=73</guid>
		<description><![CDATA[Creating a remote paging listview isn&#8217;t particularly difficult but getting to the point of knowing how all the pieces fit together can be tricky first time round. This post describes how I created a very simple remote paging listview that retrieves each page of data from an Oracle database table. As well as describing the [...]]]></description>
			<content:encoded><![CDATA[<p>Creating a remote paging listview isn&#8217;t particularly difficult but getting to the point of knowing how all the pieces fit together can be tricky first time round. This post describes how I created a very simple remote paging listview that retrieves each page of data from an Oracle database table. As well as describing the listview I&#8217;ve also included the code for the server side that actually fetches and returns each page of data to the client.</p>
<p><strong>Deploying GWT-Ext</strong><br />
If you&#8217;ve not used <a href="http://www.gwt-ext.com/">GWT-Ext</a> before then here&#8217;s how to include it in your GWT application. First you&#8217;ll need to download GWT-Ext itself. At the time of writing 2.0.4 is the latest release, you can get it <a href="http://code.google.com/p/gwt-ext/downloads/list">here</a>. The real workhorse behind GWT-Ext is the Javascript library <a href="http://extjs.com/">ExtJS</a>. GWT-Ext 2.0.4 requires ExtJS 2.0.2 which you can download from <a href="http://yogurtearl.com/ext-2.0.2.zip">here</a>.</p>
<p>If you don&#8217;t already have a GWT application then GWT&#8217;s <a href="http://code.google.com/docreader/#p(google-web-toolkit-doc-1-5)s(google-web-toolkit-doc-1-5)t(GettingStartedBasics)">The Basics</a> guide is a good place to start.</p>
<p>With your application up and running here are the steps required to deploy GWT-Ext within it.</p>
<p>1. Create the directory <code>js/ext</code> under your application&#8217;s <code>public</code> folder and copy the following files and directories from ExtJS into it,</p>
<pre><code>adapter
resources
ext-all.js
ext-all-debug.js
ext-core.js
ext-core-debug.js</code></pre>
<p>2. Edit your module file and add these lines to it,</p>
<pre><code>&lt;inherits name='com.gwtext.GwtExt'/&gt;
&lt;stylesheet src="js/ext/resources/css/ext-all.css"/&gt;
&lt;script src="js/ext/adapter/ext/ext-base.js" /&gt;
&lt;script src="js/ext/ext-all.js" /&gt;</code></pre>
<p>3. The final step it to add the GWT-Ext jar, gwtext.jar, to your application&#8217;s classpath.</p>
<p><strong>The Client Side Code</strong><br />
GWT-Ext refers to a listview as a Grid so to avoid confusion I&#8217;ll use that term throughout the rest of this post. I used the term listview up to this point because it&#8217;s probably a more generally accepted description.</p>
<p>Before going into the code it&#8217;s worth spending a little time understanding the various objects that are needed to support the Grid object. The first thing worth pointing out is that GWT-Ext doesn&#8217;t use standard <a href="http://code.google.com/docreader/#p(google-web-toolkit-doc-1-5)s(google-web-toolkit-doc-1-5)t(DevGuideRemoteProcedureCalls)">GWT Remote Procedure Calls</a> to fetch the data needed in the Grid. What you do instead is provide it with a HTTP URL from which to retrieve the data. To do this you use a <a href="http://gwt-ext.com/docs/2.0.4/com/gwtext/client/data/DataProxy.html">DataProxy</a> object. There are three types of DataProxy, the <a href="http://gwt-ext.com/docs/2.0.4/com/gwtext/client/data/HttpProxy.html">HttpProxy</a>, the <a href="http://gwt-ext.com/docs/2.0.4/com/gwtext/client/data/MemoryProxy.html">MemoryProxy</a> and the <a href="http://gwt-ext.com/docs/2.0.4/com/gwtext/client/data/ScriptTagProxy.html">ScriptTagProxy</a>. The MemoryProxy is just a static in memory data source and the ScriptTagProxy is the same as HttpProxy except that it retrieves data from a site other than the one where your Grid is hosted. So what format should the data coming back from this URL be in? Two formats are supported, JSON and XML. In order to understand the structure of this JSON or XML we need to use a <a href="http://gwt-ext.com/docs/2.0.4/com/gwtext/client/data/RecordDef.html">RecordDef</a>. This object contains <a href="http://gwt-ext.com/docs/2.0.4/com/gwtext/client/data/FieldDef.html">FieldDef</a> objects each of which describe a particular field in the data feed. Once you&#8217;ve described you data structure using a RecordDef you create either a <a href="http://gwt-ext.com/docs/2.0.4/com/gwtext/client/data/JsonReader.html">JsonReader</a> or an <a href="http://gwt-ext.com/docs/2.0.4/com/gwtext/client/data/XmlReader.html">XmlReader</a> and give it a reference to your RecordDef. Finally, to tie all these objects together you create a <a href="http://gwt-ext.com/docs/2.0.4/com/gwtext/client/data/Store.html">Store</a>. The Store is the object you present to the Grid. It uses the DataProxy to retrieve the data and and the Reader to parse it.</p>
<p>With the Store defined we can turn our attention to the Grid. To define what columns the Grid should have and where the data for each column should come from we use a <a href="http://gwt-ext.com/docs/2.0.4/com/gwtext/client/widgets/grid/ColumnModel.html">ColumnModel</a> object. This object contains a number of <a href="http://gwt-ext.com/docs/2.0.4/com/gwtext/client/widgets/grid/ColumnConfig.html">ColumnConfig</a> objects, one for each column. A ColumnConfig contains the column title, the name of the record in the RecordDef to populate the column with, it&#8217;s width, weather it&#8217;s sortable and how the data should be rendered. To create the Grid itself we use the <a href="http://gwt-ext.com/docs/2.0.4/com/gwtext/client/widgets/grid/GridPanel.html">GridPanel</a> object. You can link it to the Store and ColumnModel using either the constructor or the setter methods.</p>
<p>The final point to note is that you&#8217;ll need to add an <strong>onRender</strong> event handler to the Grid so that the first page of data is loaded when the Grid is displayed.</p>
<p>Bringing all that together here&#8217;s the class I ended up with,</p>
<pre></code>package com._17od.gwtexamples.client;

import java.util.Date;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.RootPanel;
import com.gwtext.client.core.SortDir;
import com.gwtext.client.data.DateFieldDef;
import com.gwtext.client.data.FieldDef;
import com.gwtext.client.data.HttpProxy;
import com.gwtext.client.data.IntegerFieldDef;
import com.gwtext.client.data.JsonReader;
import com.gwtext.client.data.Record;
import com.gwtext.client.data.RecordDef;
import com.gwtext.client.data.Store;
import com.gwtext.client.data.StringFieldDef;
import com.gwtext.client.util.DateUtil;
import com.gwtext.client.widgets.Component;
import com.gwtext.client.widgets.PagingToolbar;
import com.gwtext.client.widgets.Panel;
import com.gwtext.client.widgets.event.PanelListenerAdapter;
import com.gwtext.client.widgets.grid.CellMetadata;
import com.gwtext.client.widgets.grid.ColumnConfig;
import com.gwtext.client.widgets.grid.ColumnModel;
import com.gwtext.client.widgets.grid.GridPanel;
import com.gwtext.client.widgets.grid.GridView;
import com.gwtext.client.widgets.grid.Renderer;
import com.gwtext.client.widgets.grid.RowSelectionModel;

public class PersonGrid implements EntryPoint {

	public void onModuleLoad() {

		Panel panel = new Panel();
		panel.setBorder(false);
		panel.setPaddings(15);

		HttpProxy dataProxy = new HttpProxy("http://localhost:8888/persons");

        final RecordDef recordDef = new RecordDef(new FieldDef[]{
                new StringFieldDef("class"),
                new DateFieldDef("dateOfBirth", "Y-m-d"),
                new StringFieldDef("firstname"),
                new IntegerFieldDef("id"),
                new StringFieldDef("lastname"),
        });

        JsonReader reader = new JsonReader(recordDef);
        reader.setRoot("persons");
        reader.setTotalProperty("totalPerons");
        reader.setId("id");

        final Store store = new Store(dataProxy, reader, true);
        store.setDefaultSort("id", SortDir.ASC);

        ColumnConfig firstNameColumn = new ColumnConfig("First Name", "firstname", 45, true);
        ColumnConfig lastNameColumn = new ColumnConfig("Last Name", "lastname", 45, true);
        ColumnConfig dateOfBirthColumn = new ColumnConfig("Date of Birth", "dateOfBirth", 45, true, dateRender);

        ColumnModel columnModel = new ColumnModel(new ColumnConfig[] {
        		firstNameColumn,
        		lastNameColumn,
        		dateOfBirthColumn});
        columnModel.setDefaultSortable(true);

        GridPanel grid = new GridPanel();
        grid.setWidth(700);
        grid.setHeight(300);
        grid.setTitle("People");
        grid.setStore(store);
        grid.setColumnModel(columnModel);
        grid.setTrackMouseOver(true);
        grid.setLoadMask(true);
        grid.setSelectionModel(new RowSelectionModel());
        grid.setStripeRows(true);
        grid.setIconCls("grid-icon");
        grid.setEnableColumnResize(true);

        GridView view = new GridView();
        view.setForceFit(true);
        grid.setView(view);

        PagingToolbar pagingToolbar = new PagingToolbar(store);
        pagingToolbar.setPageSize(15);
        pagingToolbar.setDisplayInfo(true);

        grid.setBottomToolbar(pagingToolbar);

        grid.addListener(new PanelListenerAdapter() {
            public void onRender(Component component) {
                store.load(0, 15);
            }
        });
        panel.add(grid);

        RootPanel.get().add(panel);

  	}

    private Renderer dateRender = new Renderer() {
        public String render(Object value, CellMetadata cellMetadata, Record record, int rowIndex, int colNum, Store store) {
            return DateUtil.format((Date) value, "d-m-Y");
        }
    };

}</code></pre>
<p><strong>Where Does the Data Come From?</strong><br />
The code below is the servlet I developed to return each page of data to the Grid. It retrieves the data from an Oracle database table called “person”. It&#8217;s not particularly complicated but it does demonstrate some challenges that are worth pointing out.</p>
<pre></code>package com._17od.gwtexamples.servlets;

import java.io.IOException;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Enumeration;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import oracle.jdbc.pool.OracleDataSource;

import org.json.JSONArray;
import org.json.JSONObject;

public class GetPersonsServlet extends HttpServlet {

	private Connection connection;

	public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
		doPost(req, res);
	}

	public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException {

		// Display the parameters we're passed in for debugging purposes
		printParameters(req);

		// Get the limit and sort parameters off the request
		int start = req.getParameter("start") == null ? 1 : Integer.parseInt(req.getParameter("start"));
		int numberToReturn = req.getParameter("limit") == null ? 10 : Integer.parseInt(req.getParameter("limit"));
		String sortBy = req.getParameter("sort") == null ? "id" : req.getParameter("sort");
		String sortOrder = req.getParameter("dir") == null ? "asc" : req.getParameter("dir");

		// Create the SQL query used to retrieve the persons
		String sql = createMainSQLQuery(sortBy, sortOrder);

		ArrayList persons = null;
		try {

			// Get a connection to the Oracle database and put it on the object so that it's easily accessible
			connection = getConnection();

			// Execute the query to return the exact records requested
			persons = queryForPersonsUsingLimits(sql, start, numberToReturn);

			// Convert the list of persons into a JSON string
			JSONObject jsonDataToReturn = new JSONObject();
			JSONArray jsonPersons = new JSONArray(persons, true);
			jsonDataToReturn.put("totalPerons", getTotalNumberOfPersons(sql));
			jsonDataToReturn.put("persons", jsonPersons);

			// Write everything back to the requestor
			res.getWriter().print(jsonDataToReturn.toString(1));

		} catch (Exception e) {
			e.printStackTrace();
			throw new IOException(e);
		} finally {
			if (connection != null) {
				try {
					connection.close();
				} catch (SQLException e) {
					e.printStackTrace();
					throw new IOException(e);
				}
			}
		}

	}

	/**
	 * Create the query used to retrieve the persons from the database.
	 * @param sortBy
	 * @param sortOrder
	 * @return
	 */
	private String createMainSQLQuery(String sortBy, String sortOrder) {

		StringBuffer sql = new StringBuffer("SELECT id, firstname, lastname, dateofbirth FROM person");
		sql.append(" ORDER BY ");
		sql.append(sortBy);
		sql.append(" ");
		sql.append(sortOrder);

		return sql.toString();

	}

	/**
	 * Wrap the query passed in with some extra SQL that pulls out the exact
	 * page of persons requested, i.e. starting at record number 10 return 20
	 * records.
	 * It's vital that the query given in 'sql' has an 'order by' clause.
	 * If it didn't then the persons could be returned in a random order
	 * making it impossible to order them into pages.
	 * @param sql
	 * @param start
	 * @param numberToReturn
	 * @return
	 * @throws SQLException
	 */
	private ArrayList queryForPersonsUsingLimits(String sql, int start, int numberToReturn) throws SQLException {

		StringBuffer sqlBuffer = new StringBuffer("SELECT * FROM (SELECT a.*, ROWNUM rnum from (");
		sqlBuffer.append(sql);
		sqlBuffer.append(") a WHERE ROWNUM <= ");
		sqlBuffer.append(start + numberToReturn);
		sqlBuffer.append(") WHERE rnum > ");
		sqlBuffer.append(start);

		Statement statement = null;
		ResultSet rs = null;

		ArrayList
<person> persons = new ArrayList
<person>();

		try {

			statement = connection.createStatement();
			rs = statement.executeQuery(sqlBuffer.toString());

			while (rs.next()) {
				Person person = new Person();
				person.setId(rs.getInt("id"));
				person.setFirstname(rs.getString("firstname"));
				person.setLastname(rs.getString("lastname"));
				person.setDateOfBirth(rs.getDate("dateofbirth"));
				persons.add(person);
			}

		} finally {
			if (rs != null) {
				rs.close();
			}
			if (statement != null) {
				statement.close();
			}
		}

		return persons;

	}

	/**
	 * Return the total numbner of records that the given query will return.
	 * @param sql
	 * @return
	 * @throws SQLException
	 */
	private int getTotalNumberOfPersons(String sql) throws SQLException {

		int count = 0;

		StringBuffer sqlBuffer = new StringBuffer("SELECT COUNT(*) count FROM (");
		sqlBuffer.append(sql);
		sqlBuffer.append(")");

		Statement statement = null;
		ResultSet rs = null;

		try {

			statement = connection.createStatement();
			rs = statement.executeQuery(sqlBuffer.toString());

			while (rs.next()) {
				count = rs.getInt("count");
			}

		} finally {
			if (rs != null) {
				rs.close();
			}
			if (statement != null) {
				statement.close();
			}
		}

		return count;

	}

	/**
	 * Return a database connection from the connection poll that's stored on the servlet/application context
	 * @return
	 * @throws SQLException
	 */
	private Connection getConnection() throws SQLException {
		OracleDataSource ods = (OracleDataSource) getServletContext().getAttribute("CONNECTION_POOL");
		if (ods == null) {
			ods = createConnectionPool();
			getServletContext().setAttribute("CONNECTION_POOL", ods);
		}
		return ods.getConnection();
	}

	/**
	 * Create a database connection pool
	 * @return
	 * @throws SQLException
	 */
	private OracleDataSource createConnectionPool() throws SQLException {
        String username = "replace with db username";
        String password = "replace with db password";
        String url = "jdbc:oracle:thin:@localhost:1521:XE";

        OracleDataSource ods = new OracleDataSource();
        ods.setURL(url);
        ods.setUser(username);
        ods.setPassword(password);
        ods.setConnectionCachingEnabled(true);

        return ods;
	}

	private void printParameters(HttpServletRequest req) {
		Enumeration parameterNames = req.getParameterNames();
		System.out.println("\nRequest Parameters:");
		while (parameterNames.hasMoreElements()) {
			String parameterName = (String) parameterNames.nextElement();
			System.out.println(" " + parameterName + "=" + req.getParameter(parameterName));
		}
	}

}</code></pre>
<p>The first point of interest in doPost() is the code that takes the ordering and limit parameters off the request. This servlet is requested when the grid is first drawn or when the user clicks on the “Next Page” or “Previous Page” buttons. For the paging to work the data needs to be ordered. The <strong>sort</strong> parameter indicates what column to sort the data by. It uses the name you give the column in the RecordDef when creating the Grid. The <strong>dir</strong> parameter says what direction the ordering should be in, ascending or descending. The <strong>start</strong> parameter is the number of the first record to return and <strong>limit</strong> is the number of records to return.</p>
<p>Now that we know what the user is asking for it&#8217;s time to build the SQL query that will fetch that data. The method createMainSQLQuery() creates a query string to select the required columns and order the ResultSet.</p>
<p>In the next section we call getConnection(). This method (and createConnectionPool()) demonstrate how to create an Oracle connection pool using OracleDataSource and it&#8217;s method setConnectionCachingEnabled().</p>
<p>The method queryForPersonsUsingLimits() is where we actually execute the query. In the first section the query is wrapped in some SQL that fetches only the rows we want starting at row start and returning only numberToReturn records. I picked this up from <a href="http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html">this Ask Tom article</a>. It&#8217;s well worth a read if you&#8217;re interested in how the ROWNUM pseudocolumn works.</p>
<p>The next section takes the query ResultSet, creates a Person object for each row and puts each on an ArrayList.</p>
<p>Back in doPost() the ArrayList of Persons is serialized to JSON text using the JSON classes freely availabe <a href="http://www.json.org/java/index.html">here</a>. The method getTotalNumberOfPersons() is called to retrieve the total number of rows the query would return if we weren&#8217;t limiting the results. The reason we need this is because the Grid will have a message at the bottom saying like “Displaying 1 to 20 of 100”. The  getTotalNumberOfPersons() method reuses the SQL we created earlier and simply wraps it in a SELECT COUNT(*).</p>
<p>Finally we write the JSON text to the output stream which is sent back to the browser.</p>
<p>Deploying the servlet into a web application is simply a matter of adding <strong>servlet</strong> and <strong>servlet-mapping</strong> entries to the application&#8217;s web.xml. For testing purposed I chose to use the web application created by GWT. By default this application is located in the <strong>&lt;project dir&gt;\tomcat\webapps\ROOT</strong>. Here are the entries I added&#8230;</p>
<pre></code>&lt;servlet&gt;
    &lt;servlet-name&gt;GetPersonsServlet&lt;/servlet-name&gt;
    &lt;servlet-class&gt;com._17od.GetPersonsServlet&lt;/servlet-class&gt;
&lt;/servlet&gt;

&lt;servlet-mapping&gt;
    &lt;servlet-name&gt;GetPersonsServlet&lt;/servlet-name&gt;
    &lt;url-pattern&gt;/persons&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;</code></pre>
<p>Here&#8217;s a sample of the JSON returned from the URL http://localhost:8888/persons?start=2&#038;limit=3.</p>
<pre><code>{
 "persons": [
  {
   "class": "class com._17od.servlets.Person",
   "dateOfBirth": "1901-01-17",
   "firstname": "Laurel",
   "id": 3,
   "lastname": "Davis"
  },
  {
   "class": "class com._17od.servlets.Person",
   "dateOfBirth": "1892-05-16",
   "firstname": "Ted",
   "id": 4,
   "lastname": "Schoenberger"
  },
  {
   "class": "class com._17od.servlets.Person",
   "dateOfBirth": "2001-05-19",
   "firstname": "Rebecca",
   "id": 5,
   "lastname": "Taylor"
  }
 ],
 "totalPerons": 20
}</code></pre>
<p>The JSON implementation I&#8217;m using includes the name of the class being serialized, hence the class attribute.</p>
<p>Since I&#8217;ve included all the other code here&#8217;s the Person class,</p>
<pre></code>package com._17od.gwtexamples.servlets;

import java.util.Date;

public class Person {

	private int id;
	private String firstname;
	private String lastname;
	private Date dateOfBirth;

	public Date getDateOfBirth() {
		return dateOfBirth;
	}

	public void setDateOfBirth(Date dateOfBirth) {
		this.dateOfBirth = dateOfBirth;
	}

	public String getFirstname() {
		return firstname;
	}

	public void setFirstname(String firstname) {
		this.firstname = firstname;
	}

	public int getId() {
		return id;
	}

	public void setId(int personId) {
		this.id = personId;
	}

	public String getLastname() {
		return lastname;
	}

	public void setLastname(String lastname) {
		this.lastname = lastname;

	}

}</code></pre>
<p>To finish off here&#8217;s a screenshot of the Grid,<br />
<a href="http://localhost/17od/wp-content/uploads/2008/07/screenshot-persongrid.png"><img src="http://localhost/17od/wp-content/uploads/2008/07/screenshot-persongrid.png" alt="Grid Screenshot" title="screenshot-persongrid" width="500" height="226" class="size-full wp-image-90" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.17od.com/2008/07/24/how-to-create-a-remote-paging-listview-using-gwt-ext/feed/</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
		<item>
		<title>Meteor WebSMS</title>
		<link>http://www.17od.com/2007/10/10/meteor-websms/</link>
		<comments>http://www.17od.com/2007/10/10/meteor-websms/#comments</comments>
		<pubDate>Wed, 10 Oct 2007 19:01:46 +0000</pubDate>
		<dc:creator>Adrian</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[irish]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[junit]]></category>
		<category><![CDATA[maven]]></category>
		<category><![CDATA[meteor]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[sms]]></category>

		<guid isPermaLink="false">http://www.17od.com/2007/10/10/meteor-websms/</guid>
		<description><![CDATA[As a Java5/JUnit4/Maven2 learning exercise I&#8217;ve written a simple little SMS command line utility called Meteor WebSMS. It allows you to bypass Meteor&#8217;s website and send the free web SMS messages they offer from your command line (you have to be a Meteor customer of course). The API is completely separate from the the command [...]]]></description>
			<content:encoded><![CDATA[<p>As a Java5/JUnit4/Maven2 learning exercise I&#8217;ve written a simple little SMS command line utility called <a href="http://www.17od.com/meteor-websms">Meteor WebSMS</a>. It allows you to bypass <a href="http://www.mymeteor.ie">Meteor&#8217;s website</a> and send the free web SMS messages they offer from your command line (you have to be a Meteor customer of course).</p>
<p>The API is completely separate from the the command line tool so it&#8217;s available if you want to use it in your own Java programs. Javadocs are <a href="http://www.17od.com/meteor-websms/apidocs/index.html">here</a>.</p>
<p>Since this is a learning exercise full source code and Maven pom.xml are included in the distribution. The URL is <a href="http://www.17od.com/meteor-websms">http://www.17od.com/meteor-websms</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.17od.com/2007/10/10/meteor-websms/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>UPM 1.5 Released</title>
		<link>http://www.17od.com/2006/12/31/upm-15-released/</link>
		<comments>http://www.17od.com/2006/12/31/upm-15-released/#comments</comments>
		<pubDate>Sun, 31 Dec 2006 17:52:37 +0000</pubDate>
		<dc:creator>Adrian</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[password manager]]></category>
		<category><![CDATA[upm]]></category>

		<guid isPermaLink="false">http://www.17od.com/2006/12/31/upm-15-released/</guid>
		<description><![CDATA[Just thought I&#8217;d slip another release of UPM out before the end of the year. Here&#8217;s what&#8217;s new in this release&#8230; - Added Czech and German translations (courtesy of Petr Ustohal) - Added username and password fields to the HTTP proxy options - The HTTP Proxy can be enabled/disabled using a checkbox while still retaining [...]]]></description>
			<content:encoded><![CDATA[<p>Just thought I&#8217;d slip another release of <a href="http://www.17od.com/upm">UPM</a> out before the end of the year.</p>
<p>Here&#8217;s what&#8217;s new in this release&#8230;<br />
- Added Czech and German translations (courtesy of Petr Ustohal)<br />
- Added username and password fields to the HTTP proxy options<br />
- The HTTP Proxy can be enabled/disabled using a checkbox while still retaining the settings for later use<br />
- In the account information dialog the password is masked by default. To view it you untick a checkbox. Thanks to Jelle De Pot for suggesting this feature<br />
- Removed the French localisation (no French translator available)<br />
- Fixed a problem that caused a NullPointerException on the Mac OS X version when exiting the Options dialog after changing the language<br />
- A few small bugfixes here and there</p>
]]></content:encoded>
			<wfw:commentRss>http://www.17od.com/2006/12/31/upm-15-released/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Hiding &amp; Unhiding the text in a JPasswordField</title>
		<link>http://www.17od.com/2006/12/21/hiding-unhiding-the-text-in-a-jpasswordfield/</link>
		<comments>http://www.17od.com/2006/12/21/hiding-unhiding-the-text-in-a-jpasswordfield/#comments</comments>
		<pubDate>Thu, 21 Dec 2006 13:57:56 +0000</pubDate>
		<dc:creator>Adrian</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://www.17od.com/2006/12/21/hiding-unhiding-the-text-in-a-jpasswordfield/</guid>
		<description><![CDATA[JPasswordField is a Java SWING control used to manage password fields. Rather than use a normal JTextField it masks the characters typed by the user with some other character (default is an &#8216;*&#8217;). Very often it&#8217;s useful to see the actual text in this field. One way to do this is to give the user [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://java.sun.com/docs/books/tutorial/uiswing/components/passwordfield.html">JPasswordField</a> is a Java SWING control used to manage password fields. Rather than use a normal JTextField it masks the characters typed by the user with some other character (default is an &#8216;*&#8217;).</p>
<p>Very often it&#8217;s useful to see the actual text in this field. One way to do this is to give the user the option to have a quick peek behind the masked characters by clicking on a checkbox. After adding this feature to <a href="http://www.17od.com/upm">UPM</a> I thought it might share it since it used a feature of JPasswordField I hadn&#8217;t seen before.</p>
<p>The code below shows the action listener I placed on the checkbox used to toggle the password visibility.</p>
<pre><code>hidePasswordCheckbox.addItemListener(new ItemListener() {
    public void itemStateChanged(ItemEvent e) {
        if (e.getStateChange() == ItemEvent.SELECTED) {
            httpProxyPassword.setEchoChar('*');
        } else {
             httpProxyPassword.setEchoChar((char) 0);
        }
    }
});</code></pre>
<p>Basically the setEchoChar() method sets the character that&#8217;s displayed instead of the actual character. When you set this value to &#8220;0&#8243; JPasswordField doesn&#8217;t perform any masking. It&#8217;s that simple.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.17od.com/2006/12/21/hiding-unhiding-the-text-in-a-jpasswordfield/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>UPM 1.4 Released</title>
		<link>http://www.17od.com/2006/11/27/upm-14-released/</link>
		<comments>http://www.17od.com/2006/11/27/upm-14-released/#comments</comments>
		<pubDate>Mon, 27 Nov 2006 21:20:07 +0000</pubDate>
		<dc:creator>Adrian</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[password manager]]></category>
		<category><![CDATA[upm]]></category>

		<guid isPermaLink="false">http://www.17od.com/2006/11/27/upm-14-released/</guid>
		<description><![CDATA[Version 1.4 of UPM has just been released. The most important feature in this release is the support for multiple languages. It ships with English and French language bundles but I&#8217;d really appreciate help from anyone who can fix up the French bundle (I used Google to translate but I don&#8217;t think it worked out [...]]]></description>
			<content:encoded><![CDATA[<p>Version 1.4 of <a href="http://www.17od.com/upm">UPM</a> has just been released.</p>
<p>The most important feature in this release is the support for multiple languages. It ships with English and French language bundles but I&#8217;d really appreciate help from anyone who can fix up the French bundle (I used Google to translate but I don&#8217;t think it worked out too well) or provide other language translations. <a href="http://upm.svn.sourceforge.net/viewvc/*checkout*/upm/trunk/src/upm.properties">Here&#8217;s the resource bundle</a> that needs to be translated.</p>
<p>The full change log is&#8230;</p>
<ul>
<li>Added support for internationalisation. English and French language bundles are included.</li>
<li>Added support for editing the account name</li>
<li>Double clicking or hitting enter on an account opens the account in read-only view mode. Edit it still available on the toolbar, menu bar and as a keyboard shortcut.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.17od.com/2006/11/27/upm-14-released/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Problem with Hibernate many-to-many association</title>
		<link>http://www.17od.com/2006/11/09/problem-with-hibernate-many-to-many-association/</link>
		<comments>http://www.17od.com/2006/11/09/problem-with-hibernate-many-to-many-association/#comments</comments>
		<pubDate>Thu, 09 Nov 2006 20:59:18 +0000</pubDate>
		<dc:creator>Adrian</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[hibernate]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://www.17od.com/2006/11/09/problem-with-hibernate-many-to-many-association/</guid>
		<description><![CDATA[This post is really a record for both myself and anyone else who ever comes across the same problem. The Problem: Given two entities with a many-to-many association between them, the join table isn&#8217;t being populated when an association is made. The Problem Set: Two persistent entities, &#8220;Group&#8221; and &#8220;ActivityType&#8221; with a many-to-many association between [...]]]></description>
			<content:encoded><![CDATA[<p>This post is really a record for both myself and anyone else who ever comes across the same problem.</p>
<p><strong>The Problem</strong>: Given two entities with a many-to-many association between them, the join table isn&#8217;t being populated when an association is made.</p>
<p><strong>The Problem Set</strong>: Two persistent entities, &#8220;Group&#8221; and &#8220;ActivityType&#8221; with a many-to-many association between them. A few snippets from the relevant files&#8230;</p>
<p><em>Group.hbm.xml</em></p>
<pre>
&lt;set name="activityTypes" table="group_activity_type" cascade="save-update" lazy="true"&gt;
    &lt;key column="group_id"/&gt;
    &lt;many-to-many column="activity_type_id" class="ActivityType"/&gt;
&lt;/set&gt;
</pre>
<p><em>ActivityType.hbm.xml</em></p>
<pre>
&lt;set name="groups" table="group_activity_type" inverse="true" cascade="save-update" lazy="true"&gt;
    &lt;key column="activity_type_id"/&gt;
    &lt;many-to-many column="group_id" class="Group"/&gt;
&lt;/set&gt;
</pre>
<p><em>Group.java</em></p>
<pre>
private Set activityTypes = new HashSet();

public Set getActivityTypes() {
	return activityTypes;
}

public void setActivityTypes(Set activityTypes) {
	this.activityTypes = activityTypes;
}
</pre>
<p><em>ActivityType.java</em></p>
<pre>
private Set groups = new HashSet();

public Set getGroups() {
	return groups;
}

public void setGroups(Set groups) {
	this.groups = groups;
}
</pre>
<p><em>Some test code</em></p>
<pre>
org.hibernate.classic.Session session = HibernateUtil.getSessionFactory().openSession();
session.setFlushMode(FlushMode.MANUAL);
ManagedSessionContext.bind(session);
session.beginTransaction();

Group group = new Group();
group.setName("Test Group");
ActivityType activityType = new ActivityType();
activityType.setName("Activity Type");
activityType.getGroups().add(group);
group.getActivityTypes().add(activityType);
activityTypeDAO.saveActivityType(activityType);
groupDAO.saveGroup(group);

ManagedSessionContext.unbind(HibernateUtil.getSessionFactory());
session.getTransaction().commit();
session.close();
</pre>
<p><strong>The Solution</strong>: This took me ages to figure out but I eventually stumbled across the problem <a href="http://forum.hibernate.org/viewtopic.php?p=2328276&#038;sid=a7dc230384c6aaf1c2aa79b65f728e67">on this thread</a>. The problem is that I&#8217;m not calling session.flush() before I commit the session. Here&#8217;s the last paragraph of test code with the new flush statement.</p>
<pre>
ManagedSessionContext.unbind(HibernateUtil.getSessionFactory());
<strong>session.flush();</strong>
session.getTransaction().commit();
session.close();
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.17od.com/2006/11/09/problem-with-hibernate-many-to-many-association/feed/</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>Using Managed Sessions in Hibernate to Ease Unit Testing</title>
		<link>http://www.17od.com/2006/11/06/using-managed-sessions-in-hibernate-to-ease-unit-testing/</link>
		<comments>http://www.17od.com/2006/11/06/using-managed-sessions-in-hibernate-to-ease-unit-testing/#comments</comments>
		<pubDate>Mon, 06 Nov 2006 21:45:23 +0000</pubDate>
		<dc:creator>Adrian</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[hibernate]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[unit testing]]></category>

		<guid isPermaLink="false">http://www.17od.com/2006/11/06/using-managed-sessions-in-hibernate-to-ease-unit-testing/</guid>
		<description><![CDATA[If you&#8217;ve ever tried to reuse a session in Hibernate you may have come across this exception&#8230; org.hibernate.SessionException: Session is closed! at org.hibernate.impl.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:49) at org.hibernate.impl.SessionImpl.beginTransaction(SessionImpl.java:1319) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:301) at $Proxy0.beginTransaction(Unknown Source) .... The reason for this is that Hibernate is using &#8220;thread&#8221; managed sessions. [...]]]></description>
			<content:encoded><![CDATA[<p>If you&#8217;ve ever tried to reuse a session in <a title="Hibernate" href="http://www.hibernate.org/">Hibernate</a> you may have come across this exception&#8230;</p>
<pre>
org.hibernate.SessionException: Session is closed!
   at org.hibernate.impl.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:49)
   at org.hibernate.impl.SessionImpl.beginTransaction(SessionImpl.java:1319)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
   at java.lang.reflect.Method.invoke(Unknown Source)
   at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:301)
   at $Proxy0.beginTransaction(Unknown Source)
   ....
</pre>
<p>The reason for this is that Hibernate is using &#8220;thread&#8221; managed <a title="sessions" href="http://www.hibernate.org/hib_docs/v3/reference/en/html/architecture.html#architecture-current-session">sessions</a>. With this type of session management Hibernate manages the session for you. When you first attempt to use a session Hibernate will create one and attach it to your local thread. When you commit the transaction in the session Hibernate will automatically close the session meaning it can&#8217;t be reused.</p>
<p>To get around this problem the best option is to use &#8220;managed&#8221; sessions. With managed sessions you&#8217;re in full control of creating, flushing, commiting, and closing sessions. Here&#8217;s how.</p>
<p>In your <code>hibernate.cfg.xml</code> change the property &#8220;current_session_context_class&#8221; to &#8220;managed&#8221;.</p>
<p>To create a session and start a transaction in that session do this&#8230;</p>
<pre>
   org.hibernate.classic.Session session = HibernateUtil.getSessionFactory().openSession();
   session.setFlushMode(FlushMode.MANUAL);
   ManagedSessionContext.bind(session);
   session.beginTransaction();
</pre>
<p>To commit a transaction in the session do this&#8230;</p>
<pre>
   ManagedSessionContext.unbind(HibernateUtil.getSessionFactory());
   session.flush();
   session.getTransaction().commit();
   session.close();
</pre>
<p>To use this code in unit tests I created this base unit test class that all my unit tests extend. Whenever I want to create a new session/transaction I call the method createNewSessionAndTransaction(). To commit the session&#8217;s transaction I call the method commitTransaction().</p>
<pre>
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.context.ManagedSessionContext;
import util.HibernateUtil;
import junit.framework.TestCase;

/**
 * Abstract unit test with helper methods for managed session control
 */
public abstract class ManagedSessionUnitTest extends TestCase {

   /**
    * Create a new Session.
    *
    * For this method to work, the application managed session strategy has to
    * be enabled. This basically means that the life of a session is controlled
    * by you and and not by Hibernate.
    *
    * To enable the application managed session strategy set the property
    * hibernate.current_session_context_class to "managed".
    *
    * Within this method we create a new session and set the flush mode to
    * MANUAL. This ensures that we have full control over when the session is
    * flushed to the database.
    */
   protected org.hibernate.Session createNewSession() {
      org.hibernate.classic.Session session = HibernateUtil.getSessionFactory().openSession();
      session.setFlushMode(FlushMode.MANUAL);
      ManagedSessionContext.bind(session);
      return (org.hibernate.Session) session;
   }

   /**
    * Start a new Transaction in the given session
    * @param session The session to create the transaction in
    */
   protected void startNewTransaction(Session session) {
      session.beginTransaction();
   }

   /**
    * Shortcut method that creates a new session and begins a transaction in it
    * @return A new session with a transaction started
    */
   protected org.hibernate.Session createNewSessionAndTransaction() {
      Session session = createNewSession();
      startNewTransaction(session);
      return session;
   }

   /**
    * Commit the transaction within the given session. This method unbinds
    * the session from the session context (ManagedSessionContext), flushes
    * the session, commmits the session and then closes the session
    * @param session The session with the transaction to commit
    */
   protected void commitTransaction(Session session) {
      ManagedSessionContext.unbind(HibernateUtil.getSessionFactory());
      session.flush();
      session.getTransaction().commit();
      session.close();
   }

}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.17od.com/2006/11/06/using-managed-sessions-in-hibernate-to-ease-unit-testing/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>UPM 1.1 Released</title>
		<link>http://www.17od.com/2006/09/01/upm-11-released/</link>
		<comments>http://www.17od.com/2006/09/01/upm-11-released/#comments</comments>
		<pubDate>Fri, 01 Sep 2006 17:40:37 +0000</pubDate>
		<dc:creator>Adrian</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[password manager]]></category>
		<category><![CDATA[upm]]></category>

		<guid isPermaLink="false">http://70.103.16.167/~onesevod/?p=3</guid>
		<description><![CDATA[It&#8217;s only taken me 7 months but I&#8217;ve finally managed to release version 1.1 of UPM. This release introduces one of the main features I&#8217;ve wanted since the start of this project, database sharing. This feature allows you to keep your password databases in sync across many machines, in my case my home and work [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s only taken me 7 months but I&#8217;ve finally managed to release version 1.1 of <a href="http://www.17od.com/upm">UPM</a>. This release introduces one of the main features I&#8217;ve wanted since the start of this project, database sharing. This feature allows you to keep your password databases in sync across many machines, in my case my home and work machines.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.17od.com/2006/09/01/upm-11-released/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>Universal Password Manager</title>
		<link>http://www.17od.com/2005/12/09/universal-password-manager/</link>
		<comments>http://www.17od.com/2005/12/09/universal-password-manager/#comments</comments>
		<pubDate>Fri, 09 Dec 2005 15:20:14 +0000</pubDate>
		<dc:creator>Adrian</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[password manager]]></category>
		<category><![CDATA[upm]]></category>

		<guid isPermaLink="false">http://70.103.16.167/~onesevod/?p=6</guid>
		<description><![CDATA[After a few months of on and off development I&#8217;ve finanally managed to release the first beta of a password manager I&#8217;ve been working on. It&#8217;s called Universal Password Manager (UPM). My main reason for developing it was so that I could use the same password database on both my home (Mac) and work computers [...]]]></description>
			<content:encoded><![CDATA[<p>After a few months of on and off development I&#8217;ve finanally managed to release the first beta of a password manager I&#8217;ve been working on. It&#8217;s called <a href="http://www.17od.com/upm">Universal Password Manager (UPM)</a>.</p>
<p>My main reason for developing it was so that I could use the same password database on both my home (Mac) and work computers (Windows). I&#8217;ve been using another Java based password manager for a while now but the interface is a bit clunky and tends to get in the way.</p>
<p>This first release has all I need bar the ability to share the password database between multiple machines. The most likely solution will be that whenever the database changes on one machine it&#8217;ll be automatically uploaded to a shared location (HTTP file upload/FTP). From here the other instances of UPM will be able to download the new version when they startup.</p>
<p>Main Features</p>
<ul>
<li>Small, fast and lean</li>
<li>Written in Java and utilizing Swing for the GUI</li>
<li>Windows and Mac OS X native feeling versions available</li>
<li>Fast Account searching</li>
<li>Streamlined for those who are more comfortable using the keyboard only</li>
<li>Uses DES with 56 bit keys to encrypt the database</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.17od.com/2005/12/09/universal-password-manager/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

