Sunday, February 27, 2011

Integrating reCaptcha in wicket

Intro

Recaptcha is a very nice widget used to stop spam on your blogs or prevent automated form completion.

This post is about how you can integrate the reCaptcha in your wicket application.

Usage

We will create a panel called RecaptchaPanel. In order to use this component to your application all you'll have to do is this:

add(new RecaptchaPanel("recaptcha"));
and of course, add the component in your markup:


Implementation

Implementation is simple. All you have to do, is to follow several steps:

Add recaptcha dependency to your project


 net.tanesha.recaptcha4j
 recaptcha4j
 0.0.7

This library hides the implementation details and expose an API for dealing with recaptcha service.

Create associated markup (RecaptchaPanel.html)



  

Create RecaptchaPanel.java

import net.tanesha.recaptcha.ReCaptcha;
import net.tanesha.recaptcha.ReCaptchaFactory;
import net.tanesha.recaptcha.ReCaptchaImpl;
import net.tanesha.recaptcha.ReCaptchaResponse;

/**
 * Displays recaptcha widget. It is configured using a pair of public/private keys which can be registered at the
 * following location:
 * 
* https://www.google.com/recaptcha/admin/create
 * 
* More details about recaptcha API: http://code.google.com/apis/recaptcha/intro.html * * @author Alex Objelean */ @SuppressWarnings("serial") public class RecaptchaPanel extends Panel { private static final Logger LOG = LoggerFactory.getLogger(RecaptchaPanel.class); @SpringBean private ServiceProvider serviceProvider; public RecaptchaPanel(final String id) { super(id); final ReCaptcha recaptcha = ReCaptchaFactory.newReCaptcha(serviceProvider.getSettings().getRecaptchaPublicKey(), serviceProvider.getSettings().getRecaptchaPrivateKey(), false); add(new FormComponent("captcha") { @Override protected void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) { replaceComponentTagBody(markupStream, openTag, recaptcha.createRecaptchaHtml(null, null)); } @Override public void validate() { final WebRequest request = (WebRequest)RequestCycle.get().getRequest(); final String remoteAddr = request.getHttpServletRequest().getRemoteAddr(); final ReCaptchaImpl reCaptcha = new ReCaptchaImpl(); reCaptcha.setPrivateKey(serviceProvider.getSettings().getRecaptchaPrivateKey()); final String challenge = request.getParameter("recaptcha_challenge_field"); final String uresponse = request.getParameter("recaptcha_response_field"); final ReCaptchaResponse reCaptchaResponse = reCaptcha.checkAnswer(remoteAddr, challenge, uresponse); if (!reCaptchaResponse.isValid()) { LOG.debug("wrong captcha"); error("Invalid captcha!"); } } }); } }

Things to notice:

  • ServiceProvider - is a spring bean containing reCaptcha configurations (public key and private key). These keys are different depending on the domain where your application is deployed (by default works for any key when using localhost domain). You can generate keys here: https://www.google.com/recaptcha/admin/create

  • The RecaptchaPanel contains a FormComponent, which allows implementing validate method, containing the validation logic.

  • Because reCaptcha use hardcoded values for hidden fields, this component cannot have multiple independent instances on the same page.

This component can be easily added to wicket-stuff or any other components library.

Conclusion

This post shows how easy you can integrate recaptcha widget in your application using wicket web framework.

Saturday, February 12, 2011

Keeping Git in sync with Perforce

This post is not about how great is Git and how "great" is perforce. It doesn't matter which one you prefer, in the end it is a matter of taste. If you don't know much about Git, you could find interesting this presentation: Linus Torvalds on Git

Unfortunately, the company I'm working for has a strict policy regarding SCM usage. For some reason we are forced to use perforce. For a git lover developer this is not nice at all. That is why I started to search for a way to enjoy the Git while keeping in sync from time to time the master branch with perforce.

My first attempt was related to git p4 plugin. Unfortunately it wasn't easy at all to make it work and after several hours I gave up.

My next attempt was to use a manual approach, which can be later simplified by creating a bash script. The idea is simple: since perforce doesn't pollute each folder of the project with all scm specific files (like: .svn or .cvs), the sync is reduced to copying everything from perforce
project folder to Git project folder and vice versa.

Let's say you have a project called testProject and you keep it at the following locations:

a) perforce: /se/perforce/testProject (Lets call it A)
b) Git: /git/testProject (Lets call it B)
Let's say you have developed several new features in your Git repository and pushed the changes to origin. Now you want to sync the changes with perforce.

Here are the steps you would follow:

  1. Get the latest changes from perforce
  2. Go to folder B and create a branch in Git called 'perforce' and delete all project files from here
  3. Copy everything from A to B. Because all the files are read-only, you should change the mode using the following command:
    chmod -v 777 * -R
    
    (we don't want the files to be read-only in Git project folder)
  4. Go to folder B and merge Git 'perforce' branch with the master branch (where your latest developed changes resides). After merge succeeds, you can 'push' the synced version to perforce.
  5. Delete all the project files from folder A.
  6. Copy all the project files from folder B to A.
  7. Make all the files back to read-only in folder A
    chmod -v 755 * -R
  8. Use 'reconcile offline work' in perforce and commit the changes. Now you are done with perforce and it has the latest changes.
  9. Bring the changes from the synced 'perforce' branch to the master and since you don't need the perforce branch anymore, you can delete it:
    git checkout master
    git merge perforce
    git branch -d perforce
    

And that's it.
To automate this steps, a bash script can be created. It would have the following parameters: perforce project location and Git project location. This way you can enjoy developing in Git and be compliant with your company rules in the same time.

Monday, November 15, 2010

Wicket-ing Ajax with jQuery

Being able to register ajax callbacks is a very common use-case in almost any web application. Chances that you would need a 'loading' ajax indicator when there is a task running in the background (gmail style) is very high.
This post is about how you can register ajax callbacks for wicket with jQuery.

In order to register ajax hooks with wicket, you have several options. Each option allows you to register one of the following three callbacks: before (executed right before the ajax call is made), after (when ajax response arrives) and failure (connection error or any other kind of ajax failure).

1) First option is to call window scoped functions, defined in wicket-ajax.js, like this:
function wicketGlobalPreCallHandler() {
  alert('Before ajax call');
}
function wicketGlobalPostCallHandler() {
  alert('ajax response ready');
}
function wicketGlobalFailureHandler() {
  alert('connection error');
}

This method is pretty straightforward, but it has one drawback: it doesn't allow to register more than one callback for each type of event.

2) Register callbacks using Wicket.Ajax utility methods.
When you want to register more than one hook, you can use do as follows:

(function() {
  //define callbacks
  var preAjaxHandler = function() {}
  var successHandler = function() {}
  var failureHandler = function() {}
  //register callbacks
  Wicket.Ajax.registerPreCallHandler(preAjaxHandler);
  Wicket.Ajax.registerPostCallHandler(successHandler);
  Wicket.Ajax.registerFailureHandler(failureHandler);
})();

This method allows you to register more than one callback for the same event. This is useful when, for instance, beside showing 'loading' ajax indicator, you want to do something else, like animating the updated by ajax DOM element.

3) The last option is similar to the previous one, but it is defined in jQuery style and hides the wicket related details.

/**
 * Helper for registerin ajax callbacks.
 * This plugin is dependent on jquery.namespace
 */
(function($) {
  $.wicket = {
    /**
     * Register a callback function to be executed after wicket ajax call is complete.
     */
    ajaxCallback : function(fn) {
      this.registerCallbacks({
        onSuccess: fn
      });
      return fn;
    },
    registerCallbacks: function(arg) {
      if ($.namespaceExist('Wicket.Ajax')) {
        if (arg.onBefore) {
          Wicket.Ajax.registerPreCallHandler(arg.onBefore);
        }
        if (arg.onSuccess) {
          Wicket.Ajax.registerPostCallHandler(arg.onSuccess);
        }
        if (arg.onFailure) {
          Wicket.Ajax.registerFailureHandler(arg.onFailure);
        }
      }
    }
  }
})(jQuery);

The above script depends on jQuery.namespace.js which defines $.namespaceExist method:
(function($) {
 $.namespace = function() {
  var a = arguments, o = null, i, j, d;
  for (i = 0; i < a.length; i = i + 1) {
   d = a[i].split(".");
   o = window;
   for (j = 0; j < d.length; j = j + 1) {
    o[d[j]] = o[d[j]] || {};
    o = o[d[j]];
   }
  }
  return o;
 };
 $.namespaceExist = function(funcName) {
  if (typeof(funcName) != "string") {
   return typeof(funcName);
  }
  var fnArray = funcName.split(".");
  var namespace = [];
  for (index in fnArray) {
   namespace.push(fnArray[index]);
   var token = namespace.join(".");
   if (eval("typeof " + token + " == 'undefined'")) {
    return false;
   }
  }
  return true;
 }
})(jQuery);
And below is a small usage example:
(function($){
  //define callbacks
  var preAjaxHandler = function() {}
  var successHandler = function() {}
  var failureHandler = function() {}

  //register success handler only
  $.wicket.ajaxCallback(successHandler);
  //register each event 
  $.wicket.registerCallbacks({
    onBefore: preAjaxHandler,
    onSucces: succesHandler,
    onFailure: failureHandler
  });
}(jQuery);

The advantage of this method is that you shouldn't worry if the wicket-ajax.js exist or not. The $.namespaceExist will take care about it.

A small note about $.namespace utility: it allows you easily define a sort of javascript 'packages'. The client code can look like this:

  (function($) {
    $.namespace('com.package.util', {
      computeX: function() {},
      computeY: function() {}
    });
    $.namespace('com.package.model', {
      name: function() {},
      age: function() {}
    });
    //calling defined methods:
    com.package.util.computeX();
    com.package.util.computeY(); 
  })(jQuery);

The plugin will take care of already defined namespace and will reuse it or will create it for you if it doesn't exist yet. This can be very useful utility when you want to break your application in several independent modules.

The code can be found on github repository at the following location: https://github.com/alexo/Javascript-Utilities/tree/master/jQueryPlugins/wicket/
Or you can simply checkout the project using the following url: git@github.com:alexo/Javascript-Utilities.git

Friday, April 24, 2009

Internet Explorer Download Problem When Using HTTPS


If you develop a secure web application and have a downloads page, you may find that your page doesn't work correctly in Internet Explorer. You may receive some sort of error message even though the document is available.
The solution for this problem is to set the following response headers to empty string (same as remove these headers):
- Pragma & Cache-Control.

Here you can find also the way I've done this in wicket:


add(new StatelessLink("downloadAttachmentLink") {
@Override
public void onClick() {
RequestCycle.get().setRequestTarget(
new ResourceStreamRequestTarget(new WebExternalResourceStream("/downloads/" + getAttachmentName())) {
@Override
protected void configure(final RequestCycle requestCycle, final Response response,
final IResourceStream resourceStream) {
final WebResponse webResponse = (WebResponse) response;
//remove these headers to make it work!
webResponse.setHeader("Pragma", "");
webResponse.setHeader("Cache-Control", "");
webResponse.setAttachmentHeader(getAttachmentName());
}
});
}
});


Links: