Saturday, February 23, 2008

Powerbook survived HD-ectomy

My beloved "old" (3.5 years) powerbook (G4, 15", 1.5 GHz) started to make humming noises, especially when tilted. A couple of weeks later there were sudden hangups, it would only restart after given some cooling time.
That it finally survived my amateur-surgery - albeit somewhat battered - let me immediately regret my fancying a new macbook. Some notes:

  • I followed this guide

  • I did need to poke into the dvd-drive-slit. I found a corkscrew useful for that - Just don't apply too much force unless you actually love that slightly battered look ...

  • As replacement, I chose the Hitachi Travelstar 5K160, 5400rpm, 8MB, 2.5", 120GB, P-ATA. In Switzerland it can be had at 107 SFr (e.g. at digitec ). Subjectively it's slightly louder and noticably faster than the original.

  • When re-assembling, don't absent-mindedly put screws into the holes meant for DVI-plugs. You'll never get them back out. But, anyway, there are plenty of screws, and even with one lost in the plug-hole, it won't fall apart (I hope).

Monday, June 04, 2007

Tapestry Components in Scala

Recently, I became interested in Scala: Multiple inheritance, good support for functional programming, a nice syntax, to mention the greatest highlights.

As a first exercise, I tried to write some Tapestry-4 components in Scala to gain some real-world experience, and to see whether Tapestry's pretty extensive use of bytecode-generation would somehow break Scala's Java-compatibility. I was pretty pleased with the results:

The following is a trait to add authorisation support to arbitrary (form-)components. It controls whether to render the component into which it is mixed in and binds its disabled parameter.

package ch.marcus.components;

import org.apache.tapestry._

trait AccessControlled extends AbstractComponent {
object binding extends IBinding {
def getObject = Boolean.box(getIdPath.contains("readOnly")) // TODO: delegate to ac-service
def getObject(c: Class) = getObject
def getDescription = "AccessControl disabled-binding"
def setObject(o: Object) = {}
def isInvariant = false;
def getLocation = null;
}

override def finishLoad = {
setBinding("disabled", binding )
}

/**Derived components delegate to the desired renderComponent method here: */
def renderAccessControlled ( w: IMarkupWriter, c: IRequestCycle )

override def renderComponent( w: IMarkupWriter, c: IRequestCycle ) {
if ( ! getIdPath.contains("invisible") ) // TODO: delegate to ac-service
renderAccessControlled(w,c);
}
}


Here is how to derive an access-controlled version of the standard TextField component by inheriting from TextField and the trait we just defined:


package ch.marcus.components;

import org.apache.tapestry._
import org.apache.tapestry.form._

abstract class AuthorisedTextField extends TextField with AccessControlled {
//duplicate from scala.Object to make Tap's class-enhancer happy
@remote override def $tag(): Int = 0

override def renderAccessControlled( w:IMarkupWriter, c: IRequestCycle )
= super[TextField].renderComponent(w,c);

}

The only gotcha is the override of $tag which seems to be necessary due to a glitch in Tapestries class-enhancer. Oh, and for components with a specification(.jwc)-file this must be copied for the derived component. This is slightly annoying, but unnecessary for component that consequently use annotations (specless components).

This is how a Tapestry page looks like in Scala:

package ch.marcus.pages
import org.apache.tapestry.annotations._
import scala.reflect._

abstract class Home extends ScalaPage {
val text = "Hello, this is Scala."

@Persist
def getMbr : String
def setMbr( m:String )

def onSubmit = {
setMbr (getMbr + "x")
}
}

Note, how clean the code looks without all the syntactic noise you'd have to add in Java and how nice the Tapestry annotations (Persist) work with Scala.

Saturday, May 12, 2007

Clean and Flexible Authorisation for Tapestry Applications

Currently we'r working on an application that will be used by 4 different departments. And I can already anticipate people dropping in to casually remark someting like "Oh, by the way, we've hired this guy who needs Access to module X of the invoicing system, but of course he mustn't be allowed to press button Y, and he needs to be able to change the comment-field. And don't hurry, he'll not really need it until today afternoon."

Furthermore, the app is meant to be re-usable by subsidiaries in other countries. So putting conditionals in the view-templates (anyway bad) or cluttering component tags with disabled="ognl: admin || hasRole('blub')" kind of parameters was clearly no option.

The solution I liked best was to introduce an "@AuthorisedBlock" component which iterates over all form components it contains (in its tag-body), looks up the current users permissions (r, rw, neither) for them and, accordingly decides whether to render them enabled, disabled or not at all (see source-code at the end of this post).

The permission lookup is delegated to a simple rules engine. A rule is basically a triple (role, id-regular-expression, permission). The rules engine determines the rule with the longest pattern-match for the id-regexp - considering, of course, only rules which apply to the current user's roles.

So, in the template there's only the <span jwcid="@AuthorisedBlock"> tag enclosing the part of the template subject to authorisation checking. The actual authorisation rules can be put into a config file or into the database.

Of course, this pattern should be implementable using any web-framework with a notion of components. But Tapestry has a couple of nice features that make it particularly easy and straightforward to implement:

  • Tapestry's form-components implement a well-known, useful interface. Thus @AuthorisedBlock works with any of them without the need to build cumbersome wrappers around 3rd party library components just to support authorisation properly.

  • A component can easily control the rendering of its body, i.e. the components belonging to the child tags of its own.

  • A component takes part in the construction of a page's component trees in an oo-manner. This makes it easy to setup the binding for the disabled parameter.


In short, Tapestry makes it easy to write the rather frameworky parts of an application, because it has nicely coherent inner workings and it's very liberal in exposing these to the application programmer and letting him change them. At least, this is true for versions 3 and 4 of Tapestry. Version 5 may become a bit more secretive. I'm not sure, whether I'll actually like that. Of course, I see the intended win: A Tap 3/4 app's GUI code will typically depend quite heavily on the framework, even on parts that were perhaps never intended to be publicly (ab-)used. So, a framework update to a new major version will be hard. However, I don't think this is really harmful. After all: Your frontend code doesn't contain any business logic, right? So, when you really need to go for a major frontend framework update, what could be the reason for this need? You'll probably want to give your application a fairly complete overhaul of the whole GUI anyway and you want to do it in a new way using the new features of your new framework (-version). What's the big point in backwards compatibility then?


public abstract class AuthorisedBlock extends AbstractComponent {
@InjectObject("service:iis.web.AuthorisationSvc")
public abstract AuthorisationSvc getAuthSvc();

@Override
protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) {
renderBody(writer, cycle);
}

@Override
public void renderBody(IMarkupWriter writer, IRequestCycle cycle) {
for (int i = 0; i < _bodyCount; i++) {
IRender r = _body[i];
if ( r instanceof IFormComponent ) {
IFormComponent formComp = (IFormComponent) r;
AccessPermission permission
= getAuthSvc().permissionFor( formComp.getExtendedId() );
if ( permission == invisible )
continue;

}
cycle.getResponseBuilder().render(writer, r, cycle);
}
}

@Override
public void addBody(IRender r) {
super.addBody(r);
if ( r instanceof IFormComponent ) {
IFormComponent formComp = (IFormComponent) r;
formComp.setBinding("disabled",
new DisabledBinding(formComp.getExtendedId()) );
}
}

private class DisabledBinding implements IBinding {

private String extendedId;

public DisabledBinding(String extendedId) {
this.extendedId = extendedId;
}

public Location getLocation() {
return null;
}

public String getDescription() {
return toString();
}

public Object getObject() {
return getAuthSvc().permissionFor(extendedId) == readOnly;
}

public Object getObject(Class type) {
return getObject();
}

public boolean isInvariant() {
return false;
}

public void setObject(Object value) {
throw new UnsupportedOperationException(this+" is a read-only binding");
}

@Override
public String toString() {
return "sythetic disabled binding for " + extendedId+" to AuthorisedBlock";
}

}

}

Wednesday, April 25, 2007

Subversive beats Subclipse as SVN Plugin for Eclipse

I've switched from subclipse to subversive recently. The former used to annoy me by

  • its inability to handle directory moves/copies. I had to go and delete .svn dirs manually

  • The annoying need to do manual updates after each commit


Subversive mostly works in any of these cases. And when it doesn't want to keep track of me moving some directory around, it politely asks me to commit before continuing.

Thursday, March 08, 2007

Maven2: creating JavaDoc links behind a firewall

I just spent the better part of the morning trying to make javadoc links work. In short: configuring the proxy did not work - We have to cope with offline package-lists, not a big problem really. Of course, to configure the maven-javadoc-plugin to do this, reading the source was the quickest way to go. So put something like this in your pom.xml:

<reporting>
<plugins>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<linksource>true</linksource>
<offlineLinks>
<offlineLink>
<url>http://java.sun.com/j2se/1.5.0/docs/api/</url>
<location>T:/maven/apidoc-packagelists/jdk-1.5.0</location>
</offlineLink>
</offlineLinks>
<docfilessubdirs>true</docfilessubdirs>
<excludedocfilessubdir>CVS</excludedocfilessubdir>
</configuration>
</plugin>

The locations must be valid Java file paths, pointing to a locally accessible directory which contains the package-list file for the given url.

Thursday, February 15, 2007

XFire newbie gotcha: use the correct ServiceFactory

If you use the jaxb binding (the default with the Maven-plugin) to generate you client proxies, to actually use your service, you have to instantiate a org.codehaus.xfire.jaxb2.JaxbServiceFactory instead of an org.codehaus.xfire.service.binding.ObjectServiceFactory. Otherwise the parameter names (wsdl:part@name) will be replaced with "in0"-elements by XFire - and the server will complain about an unexpected element "in0".

Quality of Maven integration becomes decisive for OSS adoption - good bye Axis

Today I realised, how important it became for any open-source project to play nicely with the Maven repository and to get its dependencies right in its pom's .
When I had to whip up a web-service client yesterday, I started out trying Axis-2 since I knew the server impl was bases on Axis. Quickly I had to decide to either start debugging their maven-plugins dependencies (obviously wsdl4j was lost somhow), or to try something different (XFire). What I definitely not wanted was to clutter my build with Ant or, worse, CLI calls. I quickly tried XFire, its Maven plugin worked out of the box - even the library itself was a pleasant surprise (easy to use, nice Jaxb-bindings, clean generated code).