<?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>Sustainable Software Blog</title>
	<atom:link href="http://sustainablesoftware.com.au/blog/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://sustainablesoftware.com.au/blog</link>
	<description>Open Knowledge :: Social Conscience</description>
	<lastBuildDate>Tue, 19 Oct 2010 02:22:51 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>The Architecture of Granite</title>
		<link>http://sustainablesoftware.com.au/blog/?p=187</link>
		<comments>http://sustainablesoftware.com.au/blog/?p=187#comments</comments>
		<pubDate>Tue, 19 Oct 2010 02:22:51 +0000</pubDate>
		<dc:creator>Sam Stainsby</dc:creator>
				<category><![CDATA[Granite]]></category>

		<guid isPermaLink="false">http://sustainablesoftware.com.au/blog/?p=187</guid>
		<description><![CDATA[This is about Granite&#8217;s architecture in its current form. Note that Granite is not yet released, so the naming and architecture described below may change. Uniscala Granite is a web application stack with the following distinctive features: a convenient, simple to use, yet powerful embedded object database: db40 its own &#8216;static&#8217; (explained below) dependency injection,  [...]]]></description>
			<content:encoded><![CDATA[<p>This is about Granite&#8217;s architecture in its current form. Note that Granite is not yet released, so the naming and architecture described below may change.</p>
<p><span id="more-187"></span></p>
<p>Uniscala Granite is a web application stack with the following distinctive features:</p>
<ul>
<li>a convenient, simple to use, yet powerful embedded object database: <a href="http://www.db4o.com/">db40</a></li>
<li>its own &#8216;static&#8217; (explained below) dependency injection,  using <a href="http://uniscala.net/mvn/uniscala-core/uniscala-ctx/scaladocs/">Uniscala Contexts</a></li>
<li>JQuery integration through <a href="http://code.google.com/p/wiquery/">Wiquery</a></li>
<li>a module system for easy application assembly (not to be confused with Maven modules)</li>
<li>a fully integrated security framework for flexible authentication and authorisation, designed to support traditional user accounts as well as more interesting scenarios such as OAuth 2.0 (we have an experimental but working Facebook login module)</li>
</ul>
<p>In addition to Granite &#8216;Core&#8217;, there are currently these optional Granite modules:</p>
<ul>
<li>Granite Scheduler — a custom scheduler for scheduling business-level tasks, using Scala Actors under the covers</li>
<li>Granite Mailer — an email sending service based on the Java Mail API</li>
<li>Granite Users — basic user ID/password accounts stored in the primary database (under construction)</li>
</ul>
<p><strong>The Application</strong></p>
<p>So how does this all go together? Like any Wicket application, there is a main application class configured in web.xml. In  Granite you typically use the Wicket application instance for configuring global security settings, registering the Granite modules that are to be loaded as the application starts up, and perhaps overriding other application members such as the &#8216;homePage&#8217;. The rest of the initialisation occurs in the modules. A Granite application must extend the Granite &#8216;App&#8217; class (note that we avoid the class name &#8216;Application&#8217; as it has special meaning in Scala), so you might do something like this:</p>
<pre>import net.uniscala.action.ImplicitAction._
import net.uniscala.meta.text.Name
import net.uniscala.granite.{App=&gt;GraniteApp}

class App extends GraniteApp {
  override val name = Name("Granite Examples")
  override val allowedAnonymousActions
    = super.allowedAnonymousActions ++ Set(View)
  override def init = {
    registerModule(ModuleA)
    registerModule(ModuleB)
    registerModule(new ModuleC(someParameters))
    super.init // MUST be called last
  }
}</pre>
<p>The code above does three things: it give the application a name, it allows anonymous users to perform actions that require the generalised &#8216;View&#8217; action, and it registers three modules (in our example, the first two modules are singletons and the last one is instantiated and takes some configuration parameters).</p>
<p><strong>Modules</strong></p>
<p>Modules are the high-level component in a Granite application. They have a simple lifecycle. Unlike component systems such as OSGI, modules cannot be started outside of application initialisation, and cannot be stopped until the application stops running. Instead, the more dynamic aspects of your application can be provided by the resources and services that you set up in the contexts.</p>
<p>A module&#8217;s lifecycle interface is essentially:</p>
<pre> def registerModule(app:App)
 def initModule(app:App)
 def startModule(app:App)
 def destroyModule(app:App)</pre>
<p>in the order that they are called on a module. The &#8216;registerModule&#8217; method can be used for registering other modules that a module depends on. Otherwise, initialisation is generally performed in the &#8216;initModule&#8217; method. However, it is important to note that the main database is not available in the <em>context</em> until the &#8216;startModule&#8217; method is called (we discuss contexts below). The first three methods are called during application startup and the last method during application shutdown.</p>
<p>Modules typically set up or configure resources for an application. Frequently, this consists of registering <em>actions</em> and <em>views</em> (both of which we will describe  below) and often also configuring the database for new types of data. Sometimes modules will add some data to the database the first time that they run.</p>
<p><strong>Contexts</strong></p>
<p>Most things in Granite are provided with a <em>context</em>, or more specifically, a &#8216;UniversalContext&#8217; from the <a href="http://uniscala.net/mvn/uniscala-core/uniscala-ctx/scaladocs/">Uniscala Contexts</a> API, which allows accesses to the resources in the context by code such as this:</p>
<pre>context { s:MyService =&gt;
  // use the resource 's'
}</pre>
<p>We use the term &#8216;resource&#8217; fairly loosely here. Often resources like &#8216;MyService&#8217; are traits, and the context will expose a concrete implementation of the trait that has been registered in the context. For example, like this:</p>
<pre>context.register[ServiceX](new MyServiceX)</pre>
<p>where &#8216;ServiceX&#8217; is a trait and MyServiceX implements that trait. Note it is fine to register the same instance under multiple traits if it implements, say, more than one service.</p>
<p>A context used in this way is equivalent to</p>
<pre>context useOrFail { res:SomeResource =&gt;
  // use the resource 'res'
}</pre>
<p>and will throw a &#8216;ResourceUnavailableException&#8217; if there is no resource with the interface &#8216;SomeResource&#8217; registered in the context. There are also &#8216;use&#8217; and &#8216;useOrElse&#8217; methods on a context that can provide alternatives to failing if you are not sure the resource should be there. There is a lot more to the contexts API, but that can be the topic for a later article.</p>
<p>Granite actions receive a context by implementing the &#8216;MutableUniversalContextual&#8217; trait, which adds a mutable  &#8216;context&#8217; property to a class. The Granite framework will make sure the context is set before an action is invoked. Granite views may get a context in other ways  — we&#8217;ll examine this when we come to discuss views.</p>
<p>There is a root context stored in the application — typically this is what you will encounter throughout a simple Granite application, but that should not be assumed.</p>
<p>I should mention that one of the commonest uses of a context is access to the database via a db4o &#8216;ObjectContainer&#8217;:</p>
<pre>context { db:ObjectContainer =&gt;
  db.store(someItem)
}</pre>
<p>Note also that Granite&#8217; internal machinery will make sure the database transaction is committed at the end of the request, which brings us to the database:</p>
<p><strong>The Database</strong></p>
<p>For convenience, Granite comes with an embedded object database provided by Versant&#8217;s open source db4o product. Granite&#8217;s core doesn&#8217;t strictly rely on that database being used, and you could use alternative or additional databases for your application.. We already have a client project being demoed which is a reporting engine that mainly accesses logs in the MySQL database of another application. I can also envisage scenarios using more than one db4o databases — perhaps even one per user. Granite does not specifically provide extra support for any of these situations (although contexts are great places for connection pools — I will do an article on this soon).</p>
<p>Granite does provide some sugar coating for db4o. Firstly, it lazily provides a new ObjectContainer per request cycle, automatically committing that container at the end of a successful request. If an exception occurs during the request cycle though, the container is rolled back instead of being committed. Granite&#8217;s db4o operates in db4o&#8217;s so-called &#8216;embedded client-server&#8217; mode.</p>
<p>Another convenience is the <a href="http://uniscala.net/mvn/uniscala-extra/uniscala-db4o/scaladocs">Uniscala DB4O Utilities</a> library  (which can be used outside of Granite). Quite often you want to use  queries as Wicket models, but db4o&#8217;s SODA queries are not serializable. Thus we have provided our own query API, found in the &#8216;net.uniscala.db4o.query&#8217; package, that allows serialisable queries to be created and later applied to a db4o database, and has a more FP flavour:</p>
<pre>import net.uniscala.db4o.query.Query
val lookups = context { db:ObjectContainer =&gt;
  Query[MyPOJO] in db
}</pre>
<p>where &#8216;lookups&#8217; is a sequence of lightweight (ObjectContainer)=&gt;MyPOJO functions (they actually wrap db4o IDs). If you want to get the actual objects in one step:</p>
<pre>val pojos:Seq[MyPOJO] = context { db:ObjectContainer =&gt;
  Query[SomePOJO] in db map (_(db))
}</pre>
<p>Db4o is used in a specific way for Granite&#8217;s default database, and YOU NEED TO KNOW THIS is for any non-trivial Granite application:</p>
<p>Firstly, transparent activation (TA) is turned on. This means that <em>activation depth is unlimited</em> for objects that are not aware of TA. Why do we do this? We don&#8217;t want to worry about fiddling with activation depth — we consider that process fraught with potential runtime errors. Manually managing activation depth means continually tracking the internal structure of the classes involved, often negating the advantages of OO encapsulation. It is bad enough having to do this for indexes, but at least the consequence of a bad index is potential for slow, unoptimised queries, not a horrendous runtime exception, or worse, an undetected error in program logic.</p>
<p>Secondly, for those that like it, there is a problem with code instrumentation to automatically add TA awareness to classes: last time I checked (Scala 2.7.x), it didn&#8217;t work with byte code generated by the Scala compiler. So, you ask, how are we preventing the wildfire of TA from pulling too much out of the database? We use what I like to think of as a &#8216;fire break&#8217; strategy: judicious use of TA-aware <em>relationship</em> classes, typically based on one of our TA-aware base classes, such as:</p>
<pre>class Membership extends ActivatableRelationship2[User,Group]</pre>
<p>My rule of thumb is to use ordinary object references where you are linking to something that would be considered a <em>part</em> of the object (unless it is huge), and use our TA-aware relationships for anything less strong. In fact, you may find this leads to better extensibility, as relationship classes can be extended and evolve over time, whereas a plain object reference can&#8217;t. You could also use the TA-aware classes provided by the db4o API. We are still investigating this area. It may be that in Scala 2.8.0 or the upcoming 2.8.1, instrumentation will work, although I&#8217;m not that keen on byte code augmentation, so I may not use it anyway.</p>
<p>Db4o&#8217;s Scala limitations also appear to limit optimisation of native queries, so for the time being we are only recommending use of the db4o SODA query API when writing queries in Scala. Native queries can still be used, but Scala confuses db4o&#8217;s query optimisation and you will most likely end up with unoptimised queries which could make your application unexpectedly slow.</p>
<p>Overall, db4o integration is not a good as we would like, but hopefully we can address some of these issues over time. Even with the added difficulties mentioned above, it is still simpler and faster than using an ORM in our opinion.</p>
<p>Configuration of the db4o database can be done during module initialisation:</p>
<pre>context { dbConfig:ServerConfiguration =&gt;
  dbConfig.common.objectClass(classOf[Book]).objectField("title").indexed(true)
}</pre>
<p><strong>Actions</strong></p>
<p>High-level user actions are modelled by <em>actions</em> in the Granite framework. They also provide a convenient point to attach security configuration. There are two broad categories of actions: <em>global actions</em>, and action on a particular business object: <em>item actions</em>. Ultimately, actions that do something useful extend net.uniscala.action.ConcreteAction:</p>
<pre>package net.uniscala.action
trait ConcreteAction[+R] extends Action {
  def doAction:R
}</pre>
<p>There are quite a few base classes provided by Granite and the Uniscala libraries for various types of action. For example:</p>
<ul>
<li>global actions are quite often associated with some type of business object, and so will extend net.uniscala.action.TypeAction.</li>
<li>actions that implement MutableUniversalContextual will be given a context by Granite each time they are performed</li>
<li>traits like net.uniscala.granite.action.ContextualTypeAction provide a basis for the above</li>
<li>there is also a ContextualItemAction base class (ItemAction is a sub-trait of TypeAction)</li>
<li>in the package net.uniscala.granite.db4o.action, the abstract Create, Delete, Edit, QueryItems and View classes provide convenient base classes for db4o CRUD operations, and also mix in security declarations (see Security below): extending these actions may be all that is needed for simple Granite applications</li>
</ul>
<p>Global and item actions can be registered during module initialisation:</p>
<pre>context { actions:ActionRegistry =&gt;
  actions.registerAction[AddBook]
  actions.registerAction[ListBooks]
}
context { itemActions:ItemActionRegistry =&gt;
  itemActions.registerItemActionFor[Book,ViewBook](lookup =&gt; new ViewBook(lookup))
  itemActions.registerItemActionFor[Book,EditBook](lookup =&gt; new EditBook(lookup))
  itemActions.registerItemActionFor[Book,DeleteBook](lookup =&gt; new DeleteBook(lookup))
}</pre>
<p>which means they can appear in menus and and against business objects in listings.</p>
<p><strong>Views</strong></p>
<p>A <em>view</em> in Granite is a Wicket component that provides a web user interface to allow a user to perform an <em>action</em>. View are registered during module initialisation:</p>
<pre>context { views:ViewRegistry =&gt;
  views.registerView((wicketId, action:AddBook) =&gt; new AddBookForm(wicketId, action))
  views.registerView((wicketId, action:ListBooks) =&gt; new QueryItemsView[Book](wicketId, action))
  views.registerView((wicketId, action:ViewBook) =&gt; new BookView(wicketId, action))
  views.registerView((wicketId, action:EditBook) =&gt; new EditBookForm(wicketId, action))
  views.registerView((wicketId, action:DeleteBook) =&gt; new DeleteConfirmationView[Book](wicketId, action))
 }</pre>
<p><strong>Security</strong></p>
<p>Security in Granite is still under development, and I will only touch on it in a shallow way here — both because it may change ,and because this article is already long enough! I have tried to design a security API that is less jargon filled and more intuitive than most. It probably deserves an article of its own at some stage.</p>
<p>Concrete actions that require security extends net.uniscala.auth.Secured:</p>
<pre>trait Secured {
  def requiredActions:Set[Action]
}</pre>
<p>The &#8216;requiredActions&#8217; method typically returns a set of one or more net.uniscala.action.ImplictAction objects (the name &#8216;ImplictAction&#8217; has nothing to do with Scala implicits). Implicit actions are actions that don&#8217;t do actually anything in an application, but instead represent an action: they include &#8221;Create&#8217;, &#8216;View&#8217;, &#8216;Update&#8217; and &#8216;Delete&#8217; (note &#8216;Edit&#8217; is the set  composed of &#8216;View&#8217; and &#8216;Update&#8217;). Also there is &#8216;Add&#8217; and &#8216;Remove&#8217; for actions on collections. These are evaluated by an net.uniscala.auth.Authorizer (not shown here) which is located on the business object or through the context of the business object, action or view in question to see if the action is permitted (the way and order in which this is located  is under review, so this is deliberately vague). If no Authorizer is found then ultimately Granite falls back to using one of two methods in the App base class, which are by default implemented like this:</p>
<pre>def allowedActions:Set[Action] = allowedAnonymousActions
def allowedAnonymousActions:Set[Action] = Set(WicketAction.ViewPage)</pre>
<p>The first method is used for authenticated users only. The &#8216;WicketAction.ViewPage&#8217; action simply represents instantiation (but not rendering) of a Wicket page. Thus the default is very restrictive (by design), and does not to allow any action that requires the previously mentioned implicit actions (View, Edit, etc.). In many cases, you will want to override this.</p>
<p>I am currently looking at various ways of grouping actions and/or creating &#8220;secure zones&#8221;, such as an admin area for all admin actions, with their own authorisers (that might for example use roles) in order to allows a range of security measures across Granite applications .. but this is a good topic for another article.</p>
]]></content:encoded>
			<wfw:commentRss>http://sustainablesoftware.com.au/blog/?feed=rss2&#038;p=187</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Granite: a simple example step-by-step</title>
		<link>http://sustainablesoftware.com.au/blog/?p=114</link>
		<comments>http://sustainablesoftware.com.au/blog/?p=114#comments</comments>
		<pubDate>Sat, 25 Sep 2010 06:57:00 +0000</pubDate>
		<dc:creator>Sam Stainsby</dc:creator>
				<category><![CDATA[Granite]]></category>

		<guid isPermaLink="false">http://sustainablesoftware.com.au/blog/?p=114</guid>
		<description><![CDATA[As a prelude to outlining the architecture of Uniscala Granite as it currently stands, I&#8217;m going to run through creating a very simple Granite example, starting from the Granite Maven archetype, and ending with the application running on Jetty that can store, display and update meaningful data in the DB4O database. I&#8217;m going to assume [...]]]></description>
			<content:encoded><![CDATA[<p>As a prelude to outlining the architecture of Uniscala <a href="http://uniscala.net/granite/">Granite </a>as it currently stands, I&#8217;m going to run through creating a very simple Granite example, starting from the Granite Maven archetype, and ending with the application running on Jetty that can store, display and update meaningful data in the DB4O database. I&#8217;m going to assume that you&#8217;ve have already checked out <a href="http://uniscala.net/">Uniscala,</a> and built and installed it with Maven according to the <a href="http://uniscala.net/granite/dev.html">quick start guide</a>.<span id="more-114"></span></p>
<p><strong>** NOTE: There is updated code in the comments.</strong></p>
<p>If you&#8217;ve followed all of the steps in the guide for using the Maven archetype, you should already have an empty application called &#8216;myapp&#8217; that will run under Jetty — we&#8217;ll use this as  a starting point for our example.</p>
<p>We will (roughly) be recreating the simple book database example that comes with Granite (in the &#8216;granite-example&#8217; module), although it will run standalone, rather than being part of a larger  application. Refer to the book module in &#8216;granite-example&#8217; in SVN for the latest version (and typo free code!).</p>
<p>Before we start, let&#8217;s quickly look at what we have in our application&#8217;s directory:</p>
<pre>pom.xml
src/main/resources/log4j.xml
src/main/webapp/WEB-INF/web.xml
src/main/scala/net/example/App.scala
src/main/scala/net/example/Module.scala
src/main/webapp/static/...</pre>
<p>The file pom.xml, log4j.xml and web.xml should be familiar in concept to any Maven-aware, Java web application developer. Wicket users will know that web.xml contains the name of the Wicket application class, in this case &#8216;net.example.App&#8217;, and other key Wicket configuration parameters. To this, Granite adds the location of the DB4o database relative to the servlet context path, and the user name and password of Granite&#8217;s &#8216;super user&#8217; &#8211; a user that is always granted all permissions:</p>
<pre>&lt;init-param&gt;
  &lt;param-name&gt;rootDatabaseFilePath&lt;/param-name&gt;
  &lt;param-value&gt;root.db4o&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;init-param&gt;
  &lt;param-name&gt;superName&lt;/param-name&gt;
  &lt;param-value&gt;super&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;init-param&gt;
  &lt;param-name&gt;superPassword&lt;/param-name&gt;
  &lt;param-value&gt;granite456&lt;/param-value&gt;
&lt;/init-param&gt;</pre>
<p>In any serious application, to make your application secure, you <em>must</em> make sure that the super user credentials are changed from the default. For this example though, it is fine to leave the file as is.</p>
<p>The &#8216;App.scala&#8217; file contains the Wicket application:</p>
<pre>class App extends GraniteApp {
  override def name = Name("My Application")
  override def init = {
    registerModule(Module)
    super.init
  }
}</pre>
<p>There is not much going on here except registering a Granite module. Here is the module (I&#8217;ll omit a bunch of code comments and uninteresting methods and fields):</p>
<pre>case object Module extends GraniteModule {
  override val name = Name("myapp")
  override def initModule(app:GraniteApp) = {
    super.initModule(app)
    // ...
  }
  override def startModule(app:GraniteApp) = {
    super.startModule(app)
    // ...
  }
}</pre>
<p>Modules are high level components in Granite, and should not be confused with Maven modules. A simple application will often only have one module. The &#8216;initModule&#8217; and &#8216;startModule&#8217; methods represent the two stages in a module&#8217;s life cycle, both of which occur during the start-up of the application. Most of the action happens in the &#8216;initModule&#8217; method, where we register the module&#8217;s resources and services and may configure resource and services from other parts of the application. Any modules used in the application must be registered first in App. Hopefully this will become clearer for the reader as we go through a concrete example.</p>
<p><strong>Step 1: domain objects</strong></p>
<p>Here is the Book class were a going to be using to store book data with:</p>
<pre>import net.uniscala.meta.text.{Name, Named}

@serializable
class Book(var title:String, var author:String, var description:String) extends Named[Book] {

  def this() = this("", "", "")
  val created:Long = System.currentTimeMillis
  override def name = Name(title)
}</pre>
<p>&#8216;Name&#8217; and &#8216;Named&#8217; are part of the Uniscala Metadata API which is used by Granite views in listings and such — but you don&#8217;t need to worry about these at just yet. The main point is that the actual fields are nice simple types, which are easiest to store in DB4O.</p>
<p><strong>Step 2: action</strong>s</p>
<p>The next thing to think about are the <em>actions</em> you want your application to perform. In our case, we want fairly standard CRUD operations: list all books, view a book, add a book, edit a book, and delete a book. Actions are used in Granite to perform desired actions, and also to generate menus and control security. Here are the actions we want:</p>
<pre>import net.uniscala.granite.db4o.action.{QueryItems, View, Create, Edit, Delete}
import net.uniscala.ctx.UniversalContext

@serializable
class ListBooks extends QueryItems[Book]

@serializable
class ViewBook(lookup:UniversalContext=&gt;Book) extends View[Book](lookup)

@serializable
class AddBook extends Create[Book] {
  override def createItem = new Book
}

@serializable
class EditBook(lookup:UniversalContext=&gt;Book) extends Edit[Book](lookup)

@serializable
class DeleteBook(lookup:UniversalContext=&gt;Book) extends Delete[Book](lookup)</pre>
<p>The &#8216;UniversalContext&#8217; references above are a context that actions are given before they are executed. They allow actions to access resources and services in Granite, such as access to a database. In this case, we are using the context to find the items that we are acting on — typically by pulling them out of a database, as is done under the covers of the View, Edit, Delete and QueryItems superclasses above. Instances of actions will be stored in Wicket components, so they need to be serializable. Actions are registered in the module, but we will show that further below.</p>
<p><strong>Step 3: views</strong></p>
<p>Lastly, we need <em>views</em> for our actions. Views in Granite are Wicket components, usually panels, that enable the user to perform actions through the web UI. Here we show the views we&#8217;ll need:</p>
<pre>import org.apache.wicket.markup.html.basic.Label
import org.apache.wicket.markup.html.form.{Button, Form, TextField}
import org.apache.wicket.markup.html.panel.Panel
import org.apache.wicket.model.PropertyModel

import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat

import net.uniscala.action.TypeAction
import net.uniscala.granite.db4o.action.{Create, Edit, View}
import net.uniscala.granite.model.ContextualItemModel

class BookView(wicketId:String, action:View[Book]) extends Panel(wicketId) {
  def model = new ContextualItemModel(ctx =&gt; action.item)
  setDefaultModel(model)

  {
    add(new Label("title", book.title))
    add(new Label("author", book.author))
    add(new Label("description", book.description))
    val format = DateTimeFormat.mediumDateTime
    val added = new DateTime(book.created)
    add(new Label("added", format.print(added)))
  }

  def book:Book = getDefaultModelObject.asInstanceOf[Book]
}

abstract class BookForm(wicketId:String, val action:TypeAction[Book,_]) extends Panel(wicketId) {
  def model:ContextualItemModel[Book]
  setDefaultModel(model)
  def book:Book = model.getObject

  val form = new Form("form")
  form.add(new TextField("title", new PropertyModel[Book](book, "title")))
  form.add(new TextField("author", new PropertyModel[Book](book, "author")))
  form.add(new TextField("description", new PropertyModel[Book](book, "description")))
  form.add(new Button("save") {
    override def onSubmit = BookForm.this.onSubmit
  })
  add(form)

  def onSubmit:Unit = action.doAction
}

class AddBookForm(wicketId:String, action:Create[Book]) extends BookForm(wicketId, action) {
  override def model = new ContextualItemModel(ctx =&gt; action.item)
}

class EditBookForm(wicketId:String, action:Edit[Book]) extends BookForm(wicketId, action) {
  override def model = new ContextualItemModel(ctx =&gt; action.item)
}</pre>
<p>A &#8216;ContextualItemModel&#8217; is a loadable-detachable model which can receive a context and can for example loads items out of a database. Note that the view wraps the action, which is why actions need to be serializable. Like actions, views are also registered in the module , as we show below. We also want views for listing all books and deleting books, be we can use some views that Granite supplies for these, as you can see in the Module code below.</p>
<p>There also has to be Wicket HTML markup files for our views, as is the  Wicket way, but these are straightforward (you need to know Wicket!) Here is the HTML for BookView:</p>
<pre>&lt;wicket:panel&gt;
&lt;p&gt;
 &lt;b&gt;&lt;span wicket:id="title"&gt;title&lt;/span&gt;&lt;/b&gt;
 by &lt;em&gt;&lt;span wicket:id="author"&gt;author&lt;/span&gt;&lt;/em&gt; &amp;mdash;
 &lt;span wicket:id="description"&gt;description here&lt;/span&gt;
&lt;/p&gt;
&lt;p style="font-size:50%; color: gray;"&gt;
 entry added: &lt;span wicket:id="added"&gt;added date&lt;/span&gt;
&lt;/p&gt;
&lt;/wicket:panel&gt;</pre>
<p>and the HTML for BookForm:</p>
<pre>﻿&lt;wicket:panel&gt;
&lt;form wicket:id="form" action="#"&gt;
 &lt;label&gt;Title: &lt;/label&gt;&lt;input wicket:id="title" size="60" /&gt;&lt;br /&gt;
 &lt;label&gt;Author: &lt;/label&gt;&lt;input wicket:id="author" size="30" /&gt;&lt;br /&gt;
 &lt;label&gt;Description:&lt;br /&gt;
 &lt;/label&gt;&lt;input wicket:id="description" size="100" /&gt;&lt;br /&gt;
 &lt;input type="submit" wicket:id="save" value="Save" /&gt;&lt;br /&gt;
&lt;/form&gt;
&lt;/wicket:panel&gt;</pre>
<p><strong>Step 4: wire it up</strong></p>
<p>Below is the complete Module class. As well as wiring up the actions and views, we also demonstrate a little DB4O configuration by pulling out DB4O database&#8217;s ServerConfiguration from the context and adding some indexes — this is just for show as we don&#8217;t have any search features in our application yet. However, if we have search, these (entirely optional) indexes would  optimise searching by title and author.</p>
<p>We similarly access the registries for actions and views and add entries there. Note that there are two types of actions: global actions and item actions. Item actions are actions done to an &#8216;item&#8217; — typically some kind of business object (a book in our case).</p>
<pre>import com.db4o.cs.config.ServerConfiguration

import net.uniscala.meta.text.Name
import net.uniscala.granite.{App=&gt;GraniteApp}
import net.uniscala.granite.module.{Module=&gt;GraniteModule}
import net.uniscala.granite.action.{ActionRegistry, ItemActionRegistry}
import net.uniscala.granite.view.ViewRegistry
import net.uniscala.granite.db4o.view.{DeleteConfirmationView, QueryItemsView}

case object Module extends GraniteModule {
  override val name = Name("Book Example")
  override def initModule(app:GraniteApp) = {
    super.initModule(app)
    context { dbConfig:ServerConfiguration =&gt;
      val common = dbConfig.common
      val bookClass = common.objectClass(classOf[Book])
      bookClass.objectField("title").indexed(true)
      bookClass.objectField("author").indexed(true)
  }
  context { actions:ActionRegistry =&gt;
    actions.registerAction[AddBook]
    actions.registerAction[ListBooks]
  }
  context { itemActions:ItemActionRegistry =&gt;
    itemActions.registerItemActionFor[Book,ViewBook](
      lookup =&gt; new ViewBook(lookup))
    itemActions.registerItemActionFor[Book,EditBook](
      lookup =&gt; new EditBook(lookup))
    itemActions.registerItemActionFor[Book,DeleteBook](
      lookup =&gt; new DeleteBook(lookup))
  }
  context { views:ViewRegistry =&gt;
    views.registerView((wicketId, action:AddBook)
      =&gt; new AddBookForm(wicketId, action))
    views.registerView((wicketId, action:ListBooks)
      =&gt; new QueryItemsView[Book](wicketId, action))
    views.registerView((wicketId, action:ViewBook)
      =&gt; new BookView(wicketId, action))
    views.registerView((wicketId, action:EditBook)
      =&gt; new EditBookForm(wicketId, action))
    views.registerView((wicketId, action:DeleteBook)
      =&gt; new DeleteConfirmationView[Book](wicketId, action))
    }
  }
}</pre>
<p><strong>Step 5: run it!</strong></p>
<p>You can now compile your application and run it through Jetty. However, because of Granite&#8217;s default security configuration, you wont see anything interesting until you login as &#8216;super&#8217; through the special URL <a href="http://localhost:8080/login?super=1">http://localhost:8080/login?super=1</a>. After logging in (default: super/granite456) you should  see the global actions defined for your application laid out in a simple default  listing:</p>
<p style="text-align: center;"><strong><a href="http://sustainablesoftware.com.au/blog/wp-content/uploads/2010/09/menu.jpg"><img class="size-full wp-image-170 aligncenter" title="menu" src="http://sustainablesoftware.com.au/blog/wp-content/uploads/2010/09/menu.jpg" alt="" width="467" height="141" /></a></strong></p>
<p>From there you can add a book:</p>
<p style="text-align: center;"><a href="http://sustainablesoftware.com.au/blog/wp-content/uploads/2010/09/add.jpg"><img class="aligncenter size-full wp-image-172" title="add" src="http://sustainablesoftware.com.au/blog/wp-content/uploads/2010/09/add.jpg" alt="" width="622" height="258" /></a></p>
<p>and list books:</p>
<p style="text-align: left;"><a href="http://sustainablesoftware.com.au/blog/wp-content/uploads/2010/09/list.jpg"><img class="aligncenter size-full wp-image-173" title="list" src="http://sustainablesoftware.com.au/blog/wp-content/uploads/2010/09/list.jpg" alt="" width="543" height="204" /></a></p>
<p style="text-align: left;">and from the listing, corresponding to the icons, view, edit or delete books (note: the order of icons above differs, and is not the best):</p>
<p style="text-align: center;"><a href="http://sustainablesoftware.com.au/blog/wp-content/uploads/2010/09/view.jpg"><img class="aligncenter size-full wp-image-176" title="view" src="http://sustainablesoftware.com.au/blog/wp-content/uploads/2010/09/view.jpg" alt="" width="542" height="186" /></a></p>
<p style="text-align: center;">
<p style="text-align: center;"><a href="http://sustainablesoftware.com.au/blog/wp-content/uploads/2010/09/view.jpg"></a><a href="http://sustainablesoftware.com.au/blog/wp-content/uploads/2010/09/edit.jpg"><img class="aligncenter size-full wp-image-175" title="edit" src="http://sustainablesoftware.com.au/blog/wp-content/uploads/2010/09/edit.jpg" alt="" width="542" height="228" /></a></p>
<p style="text-align: center;">
<p style="text-align: center;"><a href="http://sustainablesoftware.com.au/blog/wp-content/uploads/2010/09/delete.jpg"><img class="aligncenter size-full wp-image-174" title="delete" src="http://sustainablesoftware.com.au/blog/wp-content/uploads/2010/09/delete.jpg" alt="" width="542" height="189" /></a></p>
<p style="text-align: left;">Although Granite is not released yet, I hope brave explorers who are looking at it will find the above information a useful first step. How all of this actually works underneath is a topic for a future post. There is still work to be done in polishing the views and how actions are found and flow from one to the other, and also reducing boilerplate code. For example, we have some incomplete and experimental code for view generation based on type-safe <em>schemas</em> that are hand coded or generated by reflection (or both) that could be suitable for basic user interfaces, and could eliminate the view code in our example above.</p>
]]></content:encoded>
			<wfw:commentRss>http://sustainablesoftware.com.au/blog/?feed=rss2&#038;p=114</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Announcing Uniscala Granite</title>
		<link>http://sustainablesoftware.com.au/blog/?p=77</link>
		<comments>http://sustainablesoftware.com.au/blog/?p=77#comments</comments>
		<pubDate>Wed, 22 Sep 2010 00:54:09 +0000</pubDate>
		<dc:creator>Sam Stainsby</dc:creator>
				<category><![CDATA[Granite]]></category>

		<guid isPermaLink="false">http://sustainablesoftware.com.au/blog/?p=77</guid>
		<description><![CDATA[I&#8217;m pleased to announce a new web application framework, called Granite, and an associated set of reusable libraries, called Uniscala. Please note that this is a work in progress: we are not announcing a release yet, or even a beta. A number people have started asking about the project, and so I felt it would [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m pleased to announce a new web application framework, called <a href="http://uniscala.net/granite/">Granite</a>, and an associated set of reusable libraries, called <a href="http://uniscala.net/">Uniscala</a>. Please note that this is a work in progress: we are not announcing a release yet, or even a beta. A number people have started asking about the project, and so I felt it would be helpful to let the wider world know what is going on.</p>
<p>Granite is a lightweight framework for the rapid development  of web applications. It is based on the very cool and richly featured  Apache <a href="http://wicket.apache.org/">Wicket</a> web framework. Granite uses an embedded object database that avoids the  need for SQL or Object-Relational Mappers (ORMs), and, in the Wicket  tradition, is proud of, if not smug about, its distinct lack of external  XML configuration files.</p>
<p><span id="more-77"></span></p>
<p>Granite is not designed to build large ultra-scalable systems such as Facebook and Twitter, although conceivably it could be a component  in a system like that (I sometimes dream of a world where everyone has  their own database they can take with them from one application to  another). It is more aimed at modest membership databases, staff  directories, online shops, and so on.</p>
<p>My prior experience with writing applications for Zope, and Plone, which runs on Zope, was the beginning of Granite. Zope has its own object database, ZODB, which is how I came to be addicted to this kind of persistent storage. Settling on one particular database rather than trying to abstract over many kinds also has certain advantages when it comes to both simplicity and taking full advantage of the features of a database.</p>
<p>The object database that we are using for Granite is Versant&#8217;s <a href="http://www.db4o.com/">DB4O</a>, licensed under their <a href="http://www.db4o.com/about/company/legalpolicies/docl.aspx">dOCL</a> license. Prior to the dOCL, we have been using GPL for our own components, but I believe the dOCL allows us to change to Apache 2.0. If we find the licensing intractable, we will switch to a different object database, such as <a href="http://www.neodatis.org/">NeoDatis</a> (LGPL).</p>
<p>Limitations in ZODB, and a desire for type-safe code is what led to a change from the Python-Zope world to the Java world &#8230; and the Scala world, because that is what we are coding in. Yes, Scala is the final part of the Granite trinity (Wicket-DB4O-Scala), because only Scala&#8217;s expressive power could tempt me away from Python and back to the land of type-safe languages.</p>
<p>What else to say? We really only have one simple example at the moment, and some tectonic plates of architecture are still adrift, especially with regard to the security components. There is also very little internationalisation so far &#8211; we really need an expert to guide us here. However, a client project of ours is using Granite for some things and is almost at completion, and we have a number of internal projects on the go that depend on Granite. As well as the example, there is a &#8220;quick start&#8221; <a href="http://uniscala.net/granite/dev.html">guide</a> available to shows how to check out and use the code. There is also a development Maven repository available, but it is not yet updated often enough (or tested) to recommend that developers use it. There will be further announcements on this soon.</p>
<p>I invite you to check out Granite and play with it. I apologise for the lack of general documentation, but more is coming, especially if people prompt me for it . I&#8217;m always open to discussion and suggestions, and nothing is set in stone yet. I intend to publish more entries on the finer details of how Granite hangs together, as well as some detail on the reusable components (the Uniscala libraries) that can be used outside of Granite that I hope some will find useful.</p>
]]></content:encoded>
			<wfw:commentRss>http://sustainablesoftware.com.au/blog/?feed=rss2&#038;p=77</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>coreDNA, Plone and open source</title>
		<link>http://sustainablesoftware.com.au/blog/?p=33</link>
		<comments>http://sustainablesoftware.com.au/blog/?p=33#comments</comments>
		<pubDate>Mon, 06 Sep 2010 03:22:18 +0000</pubDate>
		<dc:creator>Sam Stainsby</dc:creator>
				<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Plone]]></category>

		<guid isPermaLink="false">http://sustainablesoftware.com.au/blog/?p=33</guid>
		<description><![CDATA[I chanced upon a white paper by proprietary CMS provider, coreDNA, that can serve as a convenient catalog of all of the myths and FUD (Fear, Uncertainty and Doubt smear tactics) associated with open source detractors. The apocryphal document, Open Source vs. Closed Source (Proprietary) Software, can be found on the coreDNA website. Let me [...]]]></description>
			<content:encoded><![CDATA[<p>I chanced upon a white paper by proprietary CMS provider, coreDNA, that can serve as a convenient catalog of all of the myths and FUD (Fear, Uncertainty and Doubt smear tactics) associated with open source detractors. The apocryphal document, <em>Open Source vs. Closed Source (Proprietary) Software</em>, can be <a href="http://www.coredna.com/files/openvsclosed.coredna.pdf">found</a> on the coreDNA website. Let me dissect this paper, particularly where it is possible to make a comparison with a popular (and my favourite) open source CMS, <a href="http://plone.org/">Plone</a>.<span id="more-33"></span><strong></strong></p>
<p><strong>Cost</strong></p>
<blockquote><p>&#8220;open source software requires a certain level of expertise in order to manage content&#8221;</p></blockquote>
<p>Any CMS laden with worthwhile features requires training for proper use. The authors fail to explain that this equally applies to proprietary systems. The same can be said for other issues listed in the Cost section: the cost of support and building custom infrastructure. There is a vague implications that consumers will be &#8220;battling with service/support issues&#8221; and an unsubstantiated claim that open source systems have a &#8220;general inability to scale&#8221;, which is at odds with the vast array of open source software that powers much of the Internet&#8217;s inner workings, as well as gargantuan open source-based systems such as Facebook, Flickr and Twitter.</p>
<p>The pinnacle of non-disclosure in the Cost section is this:</p>
<blockquote><p>&#8220;Notably, open source software providers are increasingly charging for add-ons, additional services and integration.&#8221;</p></blockquote>
<p>Is coreDNA saying they won&#8217;t charge for additional services, integration, or custom add-ons? A little too good to be true I suspect. For Plone, I have never needed to purchase add-on modules for clients, since free ones abound. It is true that we have been paid to customise modules for clients, or even produce new custom components, but the same would be true for proprietary systems such as coreDNS, and your choice of developer would be vastly more limited, and thus the expense likely to be much higher. Additionally, in an open source ecosystem, with hundreds or thousands of developers, the chance that someone has already done what your client needs is much higher.</p>
<p><strong>Service and Support</strong></p>
<blockquote><p>&#8220;Open source software relies on its online community network to deliver support via forums and blogs. While there are massive, loyal and engaged online communities that users can turn to, time-poor consumers of today are familiar with the immediate service and support that enables issues to be resolved in a timely manner, and these communities cannot guarantee the high level of responsive service and support proprietary software can offer.&#8221;</p></blockquote>
<p>The larger open source projects are typically backed by foundations and corporations, and are fertile zones for support service organisation to spring up who are eager to be paid to provide exactly what the above statement suggests you can&#8217;t get: timely and responsive service and support. That&#8217;s one of the things I do for a living. It is well documented that larger open source projects receive significantly more contributions from corporations and businesses than from individuals. The service is there, but if you need more than support from online forums, then you might need to pay for it.</p>
<p><strong>Innovation</strong></p>
<blockquote><p>&#8220;customized changes to the original source code limit the future support and growth of the software&#8221;</p></blockquote>
<p>The same applies to proprietary software, except you might not have the option of modifying the source at all, and be left without the feature that you needed. Obviously, you need to look at the impact of modifying the original source code before you leap in. However, this situation is fairly rare. It is much more common to add custom layers, plugins or modules on top of the original source, without modifying it. Thus your changes are tightly contained in scope, and easier to manage into the future. It is prudent to discuss future maintenance with your developer before going down this path.</p>
<p><strong>Usability</strong></p>
<p>Open source developers might need to sit down before they read this one.</p>
<blockquote><p>&#8220;Open source software is generally developer-centric, and without system administration experience or the knowledge required to manipulate programming language, use of the software and ability to fix errors as they arise is often limited to those with technical expertise.&#8221;</p></blockquote>
<p>Again, untrue. Many of the foremost experts in computer science, systems administration and software engineering, live on the cutting edge of  open source development. The Internet&#8217;s infrastructure is mostly built with open source, as are most new programming languages such as Scala, Groovy, Python and Clojure. Popular non-Windows operating systems are also constructed from open source components: Android, Ubuntu and large parts of MacOS  for example.</p>
<blockquote><p>&#8220;Open source software has been highly criticized for its lack of usability, as generally, the technology is not reviewed by usability experts and does not cater to the vast majority of computer users.&#8221;</p></blockquote>
<p>The authors should do some research here. Plone, for example, has usability experts on its core development team.</p>
<blockquote><p>&#8220;Furthermore, open source software does not legally require documentation such as user manuals or guides, hindering the creation of such tools.&#8221;</p></blockquote>
<p>Is proprietary software legally required to be documented? Documentation can be a weak point of open source projects,especially new or small ones. On the other hand, books on Plone are coming out all the time. Plone has an official online user manual, and sites like Plone TV also provide video tutorials and the like. There are many other source of information in abundance, and also online forums and chat rooms if you are stumped. For mission critical projects, you should also think about having an ongoing contract with a Plone-friendly provider for occasional maintenance, training and general assistance.</p>
<p><strong>Security</strong></p>
<p>Everyone should be concerned about security. It is well documented that security by obscurity &#8211; that is, assuming that because your code is hidden it is more secure &#8211; is an untrue and dangerous assumption. Security guru Bruce Schneier has <a href="http://www.schneier.com/crypto-gram-9909.html">this</a> to say:</p>
<blockquote><p>&#8220;As a cryptography and computer security expert, I have never understood  the current fuss about the open source software movement.  In the  cryptography world, we consider open source necessary for good security;  we have for decades.  Public security is always more secure than  proprietary security. It&#8217;s true for cryptographic algorithms, security  protocols, and security source code.  For us, open source isn&#8217;t just a  business model; it&#8217;s smart engineering practice.&#8221; &#8211; Bruce Schneier (1999)</p></blockquote>
<p>As a prior principal engineer in the area of software security, I must agree, and declare that coreDNA&#8217;s series of scare statements about open source and lack of expertise, trojans, back doors, and lack of peer-review or validation are bunk, and more likely  prevalent in proprietary software than open source in my experience.</p>
<p><strong>Conclusion</strong></p>
<p>To conclude, keep in mind that you are locking yourself into a proprietary vendor once you select them as your CMS provider. Unlike open source systems, you generally can&#8217;t get independent development companies  in to add custom features that you want &#8211; particularly for integrating to other systems which may change or come and go over time. This can vastly reduce the options for your future overall infrastructure, and you may find it difficult or impossible to  extract your data later should you want to change to another solution. Trying to get back to an open source solution if you are dissatisfied may be a very expensive exercise.</p>
]]></content:encoded>
			<wfw:commentRss>http://sustainablesoftware.com.au/blog/?feed=rss2&#038;p=33</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Effective use of file splitting and checksums</title>
		<link>http://sustainablesoftware.com.au/blog/?p=17</link>
		<comments>http://sustainablesoftware.com.au/blog/?p=17#comments</comments>
		<pubDate>Fri, 30 Jul 2010 04:42:29 +0000</pubDate>
		<dc:creator>Sam Stainsby</dc:creator>
				<category><![CDATA[Sys Admin]]></category>

		<guid isPermaLink="false">http://sustainablesoftware.com.au/blog/?p=17</guid>
		<description><![CDATA[The Linux &#8216;split&#8217; and &#8216;shasum&#8217; (or the more specific &#8216;sha1sum&#8217;) utilities are useful for moving large amounts of data around , and yet their man pages can be obscure, especially for the practical use of checkums. For this first post on our new blog, I thought I would just do something simple that might be [...]]]></description>
			<content:encoded><![CDATA[<p>The Linux &#8216;split&#8217; and &#8216;shasum&#8217; (or the more specific &#8216;sha1sum&#8217;) utilities are useful for moving large amounts of data around , and yet their man pages can be obscure, especially for the practical use of checkums.<br />
<span id="more-17"></span><br />
For this first post on our new blog, I thought I would just do something simple that might be useful to others. I was faced with moving a large VDI file from one machine to another. The 16 GB flash drive I was using had been a bit buggy in the past, and so I decided to use checksums to make sure the transfer has worked. I apologise for the spaces in the file names &#8211; they make the command look a bit more messy. Here is the large file that was moved:</p>
<pre class="brush: bash; title: ; notranslate">
$ ls -lh ~/VirtualBox/VDI/MS\ Windows\ 7.vdi
-rw------- 1 sam sam 8.0G 2010-07-21 19:44 /home/sam/VirtualBox/VDI/MS Windows 7.vdi
</pre>
<p>Firstly, we can use &#8216;split&#8217; to move the file in smaller chunks. I chose 500 MB chunks:</p>
<pre class="brush: bash; title: ; notranslate">
$ split -a 4 -d -b 500M  ~/VirtualBox/VDI/MS\ Windows\ 7.vdi MS\ Windows\ 7.vdi.part
</pre>
<p>Here is the result, with nice numeric suffixes:</p>
<pre class="brush: bash; title: ; notranslate">
$ ls -lh
total 8.0G
-rw-r--r-- 1 sam sam 500M 2010-07-29 11:40 MS Windows 7.vdi.part0000
-rw-r--r-- 1 sam sam 500M 2010-07-29 11:40 MS Windows 7.vdi.part0001
-rw-r--r-- 1 sam sam 500M 2010-07-29 11:40 MS Windows 7.vdi.part0002
-rw-r--r-- 1 sam sam 500M 2010-07-29 11:41 MS Windows 7.vdi.part0003
-rw-r--r-- 1 sam sam 500M 2010-07-29 11:41 MS Windows 7.vdi.part0004
-rw-r--r-- 1 sam sam 500M 2010-07-29 11:41 MS Windows 7.vdi.part0005
-rw-r--r-- 1 sam sam 500M 2010-07-29 11:42 MS Windows 7.vdi.part0006
-rw-r--r-- 1 sam sam 500M 2010-07-29 11:42 MS Windows 7.vdi.part0007
-rw-r--r-- 1 sam sam 500M 2010-07-29 11:43 MS Windows 7.vdi.part0008
-rw-r--r-- 1 sam sam 500M 2010-07-29 11:43 MS Windows 7.vdi.part0009
-rw-r--r-- 1 sam sam 500M 2010-07-29 11:43 MS Windows 7.vdi.part0010
-rw-r--r-- 1 sam sam 500M 2010-07-29 11:44 MS Windows 7.vdi.part0011
-rw-r--r-- 1 sam sam 500M 2010-07-29 11:44 MS Windows 7.vdi.part0012
-rw-r--r-- 1 sam sam 500M 2010-07-29 11:44 MS Windows 7.vdi.part0013
-rw-r--r-- 1 sam sam 500M 2010-07-29 11:45 MS Windows 7.vdi.part0014
-rw-r--r-- 1 sam sam 500M 2010-07-29 11:45 MS Windows 7.vdi.part0015
-rw-r--r-- 1 sam sam 166M 2010-07-29 11:45 MS Windows 7.vdi.part0016
</pre>
<p>Now generate the checksums inside the same directory:</p>
<pre class="brush: bash; title: ; notranslate">
$ sha1sum MS\ Windows\ 7.vdi.part00* &gt; SHA1SUM
$ cat SHA1SUM
a87ce194e11535f3fd61e3942c527287d0c41a65  MS Windows 7.vdi.part0000
cd20735aafccdcf24bf1694dbd6f9711805d6180  MS Windows 7.vdi.part0001
4bb1aac9de6f58be99f68acfb19affad9478d199  MS Windows 7.vdi.part0002
201d2a430a6925f2d138ae1e9e6e76278b3951dd  MS Windows 7.vdi.part0003
91a18a9c0d33ffe5528b04162ea780615a0ee020  MS Windows 7.vdi.part0004
49f4dafd2a048ea0b0eed15224e4210c565c0ce8  MS Windows 7.vdi.part0005
9e647219587ee40a8c29c875be8134fe1e83a7f7  MS Windows 7.vdi.part0006
f4608431fbe6ba46141519c456a96019d2b78dc2  MS Windows 7.vdi.part0007
72d48624aaf3763fafa3575a531620e2ab3fa2a6  MS Windows 7.vdi.part0008
68c8ba1474b068242d1106446284d4f2bc4074a8  MS Windows 7.vdi.part0009
c28f1f00786a70da9df50ed88561c3444f5441bd  MS Windows 7.vdi.part0010
3f0ca7ab71efb670347a702258149b8e5a834893  MS Windows 7.vdi.part0011
98f4708e2b86f0bb0e0e5b59ea5beb52e104f195  MS Windows 7.vdi.part0012
fd87b77ee332f822bd216cf24bde861514c45e71  MS Windows 7.vdi.part0013
727ded9f08a0ede8ed9a5011d75308f093e44162  MS Windows 7.vdi.part0014
386f9c4168c137df52cd040ef24514d62fcf7e42  MS Windows 7.vdi.part0015
85607ffb0fb310bdb2d590475d8df40b8fb89637  MS Windows 7.vdi.part0016
</pre>
<p>You ca now copy this directory (by flash drive or networks) and check the files afterwards by running &#8216;sha1sum&#8217; inside the directory thus:</p>
<pre class="brush: bash; title: ; notranslate">
$ sha1sum --check SHA1SUM
MS Windows 7.vdi.part0000: OK
MS Windows 7.vdi.part0001: OK
MS Windows 7.vdi.part0002: OK
MS Windows 7.vdi.part0003: OK
MS Windows 7.vdi.part0004: OK
MS Windows 7.vdi.part0005: OK
MS Windows 7.vdi.part0006: OK
MS Windows 7.vdi.part0007: OK
MS Windows 7.vdi.part0008: OK
MS Windows 7.vdi.part0009: OK
MS Windows 7.vdi.part0010: OK
MS Windows 7.vdi.part0011: OK
MS Windows 7.vdi.part0012: OK
MS Windows 7.vdi.part0013: OK
MS Windows 7.vdi.part0014: OK
MS Windows 7.vdi.part0015: OK
MS Windows 7.vdi.part0016: OK
</pre>
<p>&#8230; and finally concatenate the results into the original file:</p>
<pre class="brush: bash; title: ; notranslate">
$ cat MS\ Windows\ 7.vdi.part00* &gt; /tmp/MS\ Windows\ 7.vdi
</pre>
]]></content:encoded>
			<wfw:commentRss>http://sustainablesoftware.com.au/blog/?feed=rss2&#038;p=17</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

