Sunday, December 25, 2011

Lift & OpenID. Customized login / logout form

Lately I've playing with Scala and Lift framework.
While building a web application, sooner or later you'll have to face the issue of user management. Fortunatley, Lift comes with OpenID support, to your work is  very little. I don't want to annoy you with an how-to that is already available in official Lift documentation at this URL.
What I find missing was a very user-friendly login form: the one provided expect the user to know his OpenID URL, which is very unlikely. A user may know his Google / AOL / Yahoo! credentials, but not the URL associated with them. Honestly, I'm one of them! :-)
I found a very user friendly login form built with jQuery here.
So I decided to build a Lift snippet once for all wrapping a full management of login / logout operations.
The purposes were:

  • have a user friendly login form
  • support for
    - Google
    - AOL
    - Yahoo!
    - OpenID
  • A widget embeddable in any web page to show the current user and a logout button or a link to the login page
The result is a Scala class:
import scala.xml.{ Text ⇒ T }
import scala.xml.NodeSeq
import net.liftweb.http.S

abstract class Login {
  
  /**
   * Builds the login form.
   * Parameters:
   * <ul>
   * <li><b>return_url</b>: the url the user is redirected to after successful login</li>
   * <li><b>login_enterusername</b>:</li>
   * <li><b>login_enteropenid</b>:</li>
   * <li><b>login_login</b>:</li>
   * </ul>
   */
  def login = {
    val r = S.param("return_url") openOr "/index"

    <head>
      <link rel="stylesheet" type="text/css" media="screen" href={S.param("login_css") openOr ""}/>
      <script type="text/javascript" src={S.param("jqueryopenid_js") openOr ""}></script>
      <script type="text/javascript">  { "$(function() { $(\"form.openid:eq(0)\").openid(); });" }</script>
    </head>
    <h2>{ S.?(S.param("login_title") openOr "Login") }</h2>
    <form class="openid" method="post" action={ (S.param("login_url") openOr "/openid/login") + "?ReturnUrl=" + {S.param("return_url") openOr "/index"} }>
      <div>
        <ul class="providers">
          <li class="openid" title="OpenID"><img src="/classpath/org/scz/openid/ui/images/openidW.png" alt="icon"/> <span>
                                                                                                                      <strong>{ "http://{your-openid-url}" }</strong>
                                                                                                                    </span></li>
          <li class="direct" title="Google">
            <img src="/classpath/org/scz/openid/ui/images/googleW.png" alt="icon"/>
            <span>https://www.google.com/accounts/o8/id</span>
          </li>
          <li class="direct" title="Yahoo"><img src="/classpath/org/scz/openid/ui/images/yahooW.png" alt="icon"/><span>http://yahoo.com/</span></li>
          <li class="username" title="AOL screen name"><img src="/classpath/org/scz/openid/ui/images/aolW.png" alt="icon"/><span>
                                                                                                                             http://openid.aol.com/<strong>username</strong>
                                                                                                                           </span></li>
          <li class="username" title="MyOpenID user name">
            <img src="/classpath/org/scz/openid/ui/images/myopenid.png" alt="icon"/><span>http://<strong>username</strong>.myopenid.com/</span>
          </li>
          <li class="username" title="Flickr user name">
            <img src="/classpath/org/scz/openid/ui/images/flickr.png" alt="icon"/><span>http://flickr.com/<strong>username</strong>/</span>
          </li>
          <li class="username" title="Technorati user name">
            <img src="/classpath/org/scz/openid/ui/images/technorati.png" alt="icon"/><span>http://technorati.com/people/technorati/<strong>username</strong>/</span>
          </li>
          <li class="username" title="Wordpress blog name">
            <img src="/classpath/org/scz/openid/ui/images/wordpress.png" alt="icon"/><span>http://<strong>username</strong>.wordpress.com</span>
          </li>
          <li class="username" title="Blogger blog name">
            <img src="/classpath/org/scz/openid/ui/images/blogger.png" alt="icon"/><span>http://<strong>username</strong>.blogspot.com/</span>
          </li>
          <li class="username" title="LiveJournal blog name">
            <img src="/classpath/org/scz/openid/ui/images/livejournal.png" alt="icon"/><span>http://<strong>username</strong>.livejournal.com</span>
          </li>
          <li class="username" title="ClaimID user name"><img src="/classpath/org/scz/openid/ui/images/claimid.png" alt="icon"/><span>
                                                                                                                                  http://claimid.com/<strong>username</strong>
                                                                                                                                </span></li>
          <li class="username" title="Vidoop user name">
            <img src="/classpath/org/scz/openid/ui/images/vidoop.png" alt="icon"/><span>http://<strong>username</strong>.myvidoop.com/</span>
          </li>
          <li class="username" title="Verisign user name">
            <img src="/classpath/org/scz/openid/ui/images/verisign.png" alt="icon"/><span>http://<strong>username</strong>.pip.verisignlabs.com/</span>
          </li>
        </ul>
      </div>
      <fieldset>
        <label for="openid_username">{ S.?(S.param("login_enterusername") openOr "Enter your username") }</label>
        <div>
          <span></span><input type="text" name="openid_username"/><span></span>
          <input type="submit" value="Login"/>
        </div>
      </fieldset>
      <fieldset>
        <label for="openid_identifier">{ S.?(S.param("login_enteropenid") openOr "Enter your OpenID") }</label>
        <div>
          <input type="text" name="openid_identifier"/><input type="submit" value={S.?(S.param("login_login") openOr "Login")}/>
        </div>
      </fieldset>
    </form>
  }

  /**
   * Widget shown where user is authenticated.<br/>
   * If user is logged a logout form will be shown.<br/>
   * If the user is not logged in, a link to the login page will be shown. Parameters
   * <ul>
   * <li><b>login_widget_as</b></li>
   * <li><b>login_widget_logout</b></li>
   * <li><b>login_logout_path</b></li>
   * <li><b>login_path</b></li>
   * <li><b>login_widget_login</b></li>
   * </ul>
   */
  def widget: NodeSeq = {
    if (authenticated())
      <span id="logout">
        { S.?(S.param("login_widget_as") openOr "Logged in as") }
         <span>{ currentUser() }</span>
         
        <a id="logout_button" href="#">{ S.?(S.param("login_widget_logout") openOr "Logout") }</a>
        <script>{ "$('#logout_button').click(function(){document.forms['openid_form_logout'].submit();})" }</script>
        <form style="display: none" id="openid_form_logout" method="post" action={ S.param("login_logout_path") openOr "/openid/logout" }></form>
      </span>
    else
      <span id="login"><a href={ S.param("login_path") openOr "/login" }>{
        S.?(S.param("login_widget_login") openOr "Login")
      }</a></span>
  }
  
  protected val authenticated: () => Boolean
  protected val currentUser: () => String
}

As you can see, the class is abstract and provides two vals that must be overridden:
val authenticated: ()=>Boolean
val currentUser: ()=>String
The other two functions provided builds respectively the form and the login / logout widget.
Calling this snippets you can add the parameters as specified in the scaladoc comments to customize the labels, that can be localized too.

Hope you'll find usefull

Monday, September 19, 2011

maven-compiler-plugin configuration for annotations support

Several times I encountered the same problem when building with Maven:
[INFO] [compiler:compile {execution: default-compile}]
[INFO] Compiling 1 source file to /home/stefano/works/stefanocazzola.it/sc/target/classes
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Compilation failure
/home/stefano/works/test/src/main/java/it/stefano/Test.java:[17,2] annotations are not supported in -source 1.3
(use -source 5 or higher to enable annotations)
 @Override
As I said I encountered it many times: I know it's a silly error, but every time I got to search the forums to find the soution (my short-time memory isn't so good, I admit....) so I decided to write a post about that.
The solution os very very simple. Just configure the maven-compiler-plugin like that:
<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-compiler-plugin</artifactId>
 <configuration>
  <source>1.5</source>
  <target>1.5</target>
 </configuration>
</plugin>

Friday, September 9, 2011

Cloud Foundry and the missing TLD

I just started with Cloud Foundry and... well... what to say....
WHOOOOAAAAA!
It's absolutely fantastic! I hope the BETA will end soon to switch to a "general availability" of sort.
It took just five (5) minutes to start a complete Spring web project.
The only problem I had was with the c.tld file. Initially I had the problem that Tomcat could not locate the META-INF/c.tld file.
I didnt find any precise answer on the web, but in The Big Moose Saloon gave me the right hint
The problem is that in my POM file I added the dependencies
<dependency>
 <groupId>javax.servlet</groupId>
 <artifactId>servlet-api</artifactId>
 <version>2.5</version>
</dependency>
<dependency>
 <groupId>javax.servlet.jsp</groupId>
 <artifactId>jsp-api</artifactId>
 <version>2.2</version>
</dependency>
Doing so I added these two jars to the classpath but this conflict with Tomcat.
I added them to avoid compilation issues for tag handler in Eclipse.
Obviously, you can simply remove the dependencies and disable JSP validation in Eclipse. But that's not a solution, that's a workaround. And a ugly one, in my opinion.
There is a solution and is very simple. Just tell Maven to trust these jars exist, i.e. change the scope of the dependencies
<dependency>
 <groupId>javax.servlet</groupId>
 <artifactId>servlet-api</artifactId>
 <version>2.5</version>
 <scope>provided</scope>
</dependency>
<dependency>
 <groupId>javax.servlet.jsp</groupId>
 <artifactId>jsp-api</artifactId>
 <version>2.2</version>
 <scope>provided</scope>
</dependency>

Done!

Tuesday, September 6, 2011

Test driven development with Maven and Scala

As projects grow on size and complexity, you can feel a little lost on every issue or bug to fix.
It's very common to fix a bug today and, after few months when project needs to meet one more requirement (due to the fantasy of the customer or of the developer himself...), encouter the same problem once again. It's called "regression".
To avoid this unpleasant situation, you can rely on some tools to perform automatic tests: Java provides JUnit (I use JUnit4) and Scala provides Scalatest.
Obviously you can run the tests invoking the correct tool by command-line, but with Maven you can integrate all the phases of the compilation-testing task in order to have your project compiled and tested (and eventually also deployed to a repo) with one single command.
To do so, you'll have to change a little bit your POM file.
You'll also need to write your tests in the correct manner, but there are official example from the Scalatest team linked at the end of the article.

POM modifications
First add some repository...
<repositories>
 <repository>
  <id>scala-tools.org</id>
  <name>Scala-Tools Maven2 Repository</name>
  <url>http://scala-tools.org/repo-releases</url>
 </repository>
</repositories>

<pluginRepositories>
 <pluginRepository>
  <id>scala-tools.org</id>
  <name>Scala-Tools Maven2 Repository</name>
  <url>http://scala-tools.org/repo-releases</url>
 </pluginRepository>
</pluginRepositories>
Now, the dependencies. You must add the dependency
<dependency>
 <groupId>org.scalatest</groupId>
 <artifactId>scalatest_${scala.version}</artifactId>
 <version>1.6.1</version>
 <scope>test</scope>
</dependency>
I assume you set a property named scala.version valued with the Scala version you are using. Please note the version of this artifact vary with Scala version. I'm using the 2.9.0-1 version of Scala: if you are using another version, check the repo http://scala-tools.org/repo-releases
Optionally, you can remove the dependency
org.scala-tools.testing:specs_${scala.version}
Then jump to the build section.
<build>
 <sourceDirectory>src/main/scala</sourceDirectory>
 <testSourceDirectory>src/test/scala</testSourceDirectory>
</build>
Last thing to do: add the Surfire plugin. Add in the root:
<reporting>
 <plugins>
  <plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-surefire-plugin</artifactId>
   <configuration>
    <scalaVersion>${scala.version}</scalaVersion>
    <includes>
     <include>**/*Suite.class</include>
     <include>**/*Spec.class</include>
    </includes>
   </configuration>
  </plugin>
 </plugins>
</reporting>
Wow! Now you are ready to start writing your tests!

How to write tests
Writing tests using JUnit3
Writing tests using JUnit4
Writing tests using Scalatest FunSuite features
Writing tests using Scalatest Spec features

Executing the tests
Maven will execute these tests whenever you compile, deploy or simply test your projects.
Be carefull to clean your project always before compiling or testing.
For example, the command line to launch tests only (obviously, the project will be compiled too) is
mvn clean test
from the folder containing the POM.xml of your project

Hope you'll find usefull,
S

Friday, September 2, 2011

Nexus - Error 400 redeploying artifact

This is really one of the silliest error I've ever found.
This is stupid because of its solution that is so simple that generally a programmer doesn't think. In the commpon developer's mind (therefore in mine too) every error must be a serious system problem (if you are using Windows could even be a virus!), or some other programmer must have done something wrong, or maybe it's just inexplicable and depends on unknown factors.
In fact, as I said the solution is the simplest one you can think: just configuration of the repo.
You have to
  • open your Nexus server as admin
  • open configuration window of your repo
  • find the parameter "Deployment policy" and set it to "Allow redeploy"
That's it.

Friday, August 26, 2011

Scala: The Manifest

Scala, as Java, implements generics using type-erasure.
The JVM has no knowledge of the concept of "generic type", only the compiler does. As an example a List<Integer> will be compiled to non-generic List, and all type checks will be performed at compile-time.
At the contrary, C# uses reified type.

I do not want to discuss this subject here, but with reification a type-parameter is a class and you can use it as a class
class Cl1<T>{
    public void m(){
        return new T()
    }
}
whereas in Java (or Scala) you can't.
Obviously, there are pros and cons for each approach.
But is's definitely true that a C#-like behavior is often very welcome (in the factory method pattern, for example).
Working with Scala, you can take advantage of the implicit parameters and use the Manifest class.
class Cl1[T]{
  def m()(implicit mf: Manifest[T]) = {
    val cl = mf.erasure.asInstanceOf[Class[T]]
    cl.newInstance
  }
}
This is a little more verbose than C#, but satisfies the demand.
But there is one limitation: you can use this only on non-generic classes.
class Cl2[T] extends Cl1[T]{
  def m2 = m
}
Well, it could look correct but the compiler doesn't agree with you. That is because type T cannot be understood at compile-time (could be Int, String or whatever). You must specify the implicit parameter as long as you are working with generic types.
In fact if you write
class Cl2 extends Cl1[String]{
  def m2 = m
}
the compiler understands that the implicit parameter is Manifest[String] and doesn't need any other information.

Wednesday, August 24, 2011

Scala - The singleton pattern

As a first example of how to use Scala, I want to show to you how you could implement the singleton pattern.
This is a basic pattern used when you want to ensure that no more than one instance of a class can be living in your application at the same time. A method, usually called "instance", is used to retrieve this single instance. Also, the lazy pattern is recommended: do not create the singleton until it's needed.

First, how a Java singleton looks like? Well...
public class Singleton {
    private static final class SingletonHolder{
        private static final Singleton INSTANCE = new Singleton();
    }
   
    private Singleton() {}
   
    public static final Singleton instance(){
        return SingletonHolder.INSTANCE;
    }
}

Instead, with Scala it would look like.
class Singleton private () {

}

object Singleton {
  private lazy val INSTANCE =  new Singleton()
  
  def instance = INSTANCE
}

That's all

Installing Sonatype Nexus on a Linux server

Recently I had to setup Nexus on my Ubuntu machine. The process is quite straightforward, but I'll try to describe every step as if using a Linux server (without GUI, only shell commands): this could scare the basic Linux user, but trust me, it isn't difficult.
Let's get started.
  1. Download nexus with wget and place in a folder of your choice. Let's assume we are installing Nexus on /usr/local/nexus and unpack it in the same folder
    cd /usr/local
    sudo mkdir nexus
    sudo wget http://nexus.sonatype.org/downloads/nexus-oss-webapp-1.9.2-bundle.tar.gz
    sudo tar -xvzf nexus-oss-webapp-1.9.2-bundle.tar.gz
    
    I'm assuming you have a JAVA_HOME var configured and that this is attached to the PATH variable (ie: you hava Java installed and running. If you want to know how to install Java, I've wrote a simple step-by-step guide
  2. Set up some variable (using the global profile).
    sudo gedit /etc/profile
    
    and add these lines
    export NEXUS_PLATFORM=linux-x86-32 # specify your platform here
    export NEXUS_HOME=/usr/local/nexus/nexus-oss-webapp-1.9.2
    export NEXUS_EXEC=$NEXUS_HOME/bin/jsw/$NEXUS_PLATFORM
    export PATH=$NEXUS_EXEC:$PATH
  3. Now we have nexus downloaded and installed. This will not suffice because the Nexus won't start as a service. To accomplish this task we need to do something more. First, we need to modify the file that launches Nexus
    sudo gedit $NEXUS_EXEC/nexus
    
    and modify these lines
    # Application
    APP_NAME="nexus"
    APP_LONG_NAME="Sonatype Nexus OSS"
    
    # Wrapper
    WRAPPER_CMD=/usr/local/nexus/nexus-oss-webapp-1.9.2/bin/jsw/linux-x86-32/wrapper
    WRAPPER_CONF=/usr/local/nexus/nexus-oss-webapp-1.9.2/bin/jsw/conf/wrapper.conf
    
    # Priority at which to run the wrapper.  See "man nice" for valid priorities.
    #  nice is only used if a priority is specified.
    PRIORITY=
    
    # Location of the pid file.
    PIDDIR="/var/run"
  4. Now you have to set up Nexus as a service. There are two ways to do so:
    • copy the nexus launcher in /etc/init.d, make it executable and update system services
      sudo cp $NEXUS_EXEC/nexus /etc/init.d/nexus
      sudo chmod 755 /etc/init.d/nexus
      sudo update-rc.d nexus defaults
    • create a launcher, make it executable and update system services
      sudo gedit /etc/init.d/nexus # file does not exist, will be created
      
      add the lines
      COMMAND_LINE="/usr/local/nexus/nexus-oss-webapp-1.9.2/bin/jsw/linux-x86-32/nexus start"
      eval $COMMAND_LINE
      
      then
      sudo chmod 755 /etc/init.d/nexus
      sudo update-rc.d nexus defaults
You are done! Restart your system and start working with your new Nexus repository. Note that if you want to import and old repository, you just need to overwrite the /usr/local/nexus/sonatype-work folder

Friday, August 19, 2011

Installing JDK on Ubuntu 11.04 (Natty Narwhal)

  1. Remove open-jdk (optional)
    sudo apt-get remove openjdk-6-jre
    sudo apt-get autoremove
    
  2. Add repository. You can browse http://archive.canonical.com to know which versions are supported. I'm working with a fresh install of Natty Narwal(11.04)
    sudo add-apt-repository "deb http://archive.canonical.com/ natty partner"
    sudo apt-get update
    
  3. Install software
    sudo apt-get install sun-java6-jre
    sudo apt-get install sun-java6-jdk
    sudo apt-get install sun-java6-plugin
    sudo apt-get install sun-java6-fonts
    
  4. Set global variables editing file /etc/profile
    sudo gedit /etc/profile
    and add the following lines
    export JAVA_HOME = /usr/lib/jvm/java-6-sun
    export PATH = $JAVA_HOME:$PATH
    
    Alternatively you can do the same on /home/[user]/.profile

The JDK will be placed in /usr/lib/jvm/java6-sun.

If you didn't execute step 1, you may need to tell the system which alternative to use with:
sudo update-alternatives --config java
and select the new installation (you'll be prompt with altenatives, don't worry).