jeudi 1 décembre 2011

Install CloudFoundry VMC (command line client) on Debian Squeeze

We will install ruby, gems than vmc. At the end we will push a nodejs app to the cloud.
$ sudo aptitude install ruby-full
$ ruby -v
ruby 1.8.7 (2010-08-16 patchlevel 302) [x86_64-linux]

$ sudo aptitude install rubygems
$ gem -v
1.3.7

$ sudo gem install vmc
Add this line to the ~/.bash_profile
export PATH=$PATH:/var/lib/gems/1.8/bin/
$ vmc -v
vmc 0.3.13

$ vmc target api.cloudfoundry.com
Succesfully targeted to [http://api.cloudfoundry.com]

$ vmc login
Email: your.email@gmail.com
Password: ********
Successfully logged into [http://api.cloudfoundry.com]
Now, follow steps described here.

cd to the directory where you have your cloudfoundry app and run:
vmc push any_app_name_here
Resource:
http://blog.cloudfoundry.com/post/4737632136/what-happens-when-you-vmc-push-an-application-to-cloud

mercredi 2 novembre 2011

console.log Cannot call method 'log' of null

In this post I'll tell you how a simple console.log in my Chrome extension took me 3 hours to figure out why it was throwing the following error:
Uncaught TypeError: Cannot call method 'log' of null
logcommon.js:130
(anonymous function)database.js:68
common.js:130

The short story is that the console.log was inside a database callback function that was called while I was moving to another page.

The long story is :

- The user open the extension popup, and try to log in.
- If login was successful we store in the database his login and status and move the user to the protected content, in our case it is the sms sending page.
if(user.connected){
  database.transaction(function(tx) {
    console.log("Going to persist user with login : "+user.login + " and status " + user.status) ;
    tx.executeSql('INSERT INTO USER (LOGIN,STATUS) VALUES (?, ?)',
                  [user.login, user.status],
                  function(){
                    //because of the window.location below this line will suck
                    console.log("insert ok"); //doesn't work 
                   //Uncaught TypeError: Cannot call method 'log' of null
                  },
                  function(){
                   console.log("insert ko");
                  }); 
   }
  //culprit
  window.location = 'send-sms.html';
}

If we comment the window.location line, the console.log("insert ok") works.

So the bottom line is: Delegate callback work and in general any long-running script to the background page:

chrome.extension.getBackgroundPage().do_Heavy_Work();

Let's go back adding more good stuff into smshare ;)

samedi 21 mai 2011

Why I prefer Hibernate Session API to JPA EntityManager

In one of my projects, I needed to integrate @prePersist and @PreUpdate to take care of LastModified field (insert and update) using hibernate implementation of JPA with sessionFactory.

I was using spring framework with hibernate (sessionFactory), and it seems that is impossible, because these are JPA callbacks that won't work with Session API, and to catch them we must use JPA implementation.

See http://stackoverflow.com/questions/4133287/preupdate-and-prepersist-in-hibernate-jpa-using-session

Hum, it seems that is time to move to full JPA and using standards seems to be a good thing. My application will be more portable (I never had an intention to switch from my good lightweight tomcat server to any of these big application server). What other? Ah, I can at anytime change the implementation (even if I really don't have any intention to move from Hibernation to TopLink or any other ORM).

Anyway, standard is standard and we all love standards (We will see that this is not always true)

So, I started working on a new branch: refact/entityManager and after 4 days refactoring the services/dao classes from Hibernate (Hibernate/SessionFactory) to JPA (Hibernate/EntityManager),
I just discovered that when inserting a duplicate entry, the transaction is rolled back and a TransactionSystemException is thrown and there is no mean to what was the culprit (the original cause). With session API I was able to know exactly what happened. ConstraintViolationException or DataIntegrityViolationException if spring exception translation is tuned on.

As I don't want to add a try-catch clause to the top-level method calling my transactional service method, and I don't want an extra select to check if a the entry already exists, I prefer the session API behavior and I will move back to Session API.

See thread on http://stackoverflow.com/questions/6040787/spring-exception-translation-isnt-working-with-jpa

So, how did you solved your problem then?

The solution is to use @Version as described here: http://notatube.blogspot.com/2010/03/hibernate-using-event-listener-to-set.html

private Timestamp lastModified;
 @Version
 public Timestamp getLastModified() {
  return lastModified;
 }
 public void setLastModified(Timestamp lastModified) {
  this.lastModified = lastModified;
 }
 
Be sure to not have a method named onUpdate, as it is never called but make @Version fail silently. Change the method name, drop the table and create if you want @Version to work again"


Now, I'm happy with my good Hibernate/sessionFactory. May be one day, I'll try JDO, but I'll never play again with JPA/entityManager.

Extra Bonus: I don't want to loose my work

When in refact/entityManager branch, I have created a lot of test classes and I don't want to loose them.

Hopefully, git was there, suppose you are in master, just use:
git checkout refact/entityManager path/to/files ... 
sometimes we can check content of files from other branchs :
git show refact/entityManager:file
Source: http://stackoverflow.com/questions/449541/how-do-you-merge-selective-files-with-git-merge/1355990#1355990

mardi 22 mars 2011

Push to individual users with atmosphere framework and GWT

Warning
This is really a draft. It is just working but I'm sure there is better way to do that. Need approving from more experimented atmosphere developers.


The idea behind pushing to individual user is to assign a broadcaster with an id (you get the id from the request parameter for example) to atmosphere resource representing a user (browser). The broadcaster need to be added to the broadcaster factory. Now, at anytime you can get this broadcaster by issuing a lookup by id.


In the previous post we have seen how to setup atmosphere-gwt-demo project under Eclipse. When your run the atmosphere-gwt-demo project you get a window that shows 6 buttons: post request, poll, broadcast, open child window, send to the child window and a stop comet button.


In this post, I'll describes steps I followed to find my way to push to individual users.

The scenario is simple:

An external server send notifications as http post request with one parameter that identifies the target user, the atmosphereHandler receives the notification and send a message to the appropriate browser (The one that has login with the same id)

AtmosphereClient initialization

AtmosphereClient initialization happens in the GWT entry point
org.atmosphere.samples.client.GWTDemo

In the onModuleLoad we call the initialize() method:

public void initialize() {

        MyCometListener cometListener = new MyCometListener();

        AtmosphereGWTSerializer serializer = GWT.create(EventSerializer.class);
        // set a small length parameter to force refreshes
        // normally you should remove the length parameter
        System.out.println("Client url is "+GWT.getModuleBaseURL());
        
//retrieve the login from the browser GET request 
        String login = com.google.gwt.user.client.Window.Location.getParameter("login");
        client = new AtmosphereClient(GWT.getModuleBaseURL()+"gwtComet"+"?login="+login, serializer, cometListener);
        
        client.start();
    }

The AtmosphereClient establish a connection with an atmosphereHandler at this url
GWT.getModuleBaseURL()+"gwtComet
We are using a cometListener which listens to events coming from the atmosphereHandler through the onMessage(), onHeartbeat(), onError(), etc...

I've add the login request parameter to identify each browser(user)

The server side: The atmosphereHandler

When you call client.start(), the onRequest() method of the atmosphereHandler get called

@Override
public void onRequest(AtmosphereResource resource) throws IOException {
HttpServletRequest request = resource.getRequest();


String login_key = "login";
String login_value = request.getParameter(login_key);
if(login_value != null){
    //add this broadcaster to the broadcasterFactory
    addBroadcasterToFactory(resource, login_value);
}


//external server request
if (request.getMethod().equalsIgnoreCase("POST")) {
 String for_key="for";
 String for_value = request.getParameter(for_key);
 
 if(for_value != null && for_value.length() > 0){
  System.out.println("Received a notification, going to notify user "+for_value);
  
  // Single Connection/Session
  Broadcaster singleBroadcaster= BroadcasterFactory.getDefault().lookup(DefaultBroadcaster.class, for_value);
            
  singleBroadcaster.broadcast("My first push. User "+for_value);
  
  return;
 }
}

super.onRequest(resource);
}
In this method we are trying to add a broadcaster to the BroadcasterFactory with the id of the user being login in.

In the other hand, if the request is a post (Notification from external server) we lookup the broadcaster then push something.

The addBroadcasterToFactory is as follow:

public void addBroadcasterToFactory(AtmosphereResource resource, String broadcasterID){
     
  Broadcaster singleBroadcaster= BroadcasterFactory.getDefault().lookup(DefaultBroadcaster.class, bdct);
  if(singleBroadcaster != null){
   resource.setBroadcaster(singleBroadcaster); //each browser opened by this user will receive the same push message
   return false;
  }
     
     Broadcaster selfBroadcaster = new DefaultBroadcaster(broadcasterID);
     selfBroadcaster.setID(broadcasterID);
     resource.setBroadcaster(selfBroadcaster);

     boolean added = BroadcasterFactory.getDefault().add(selfBroadcaster, broadcasterID);
     System.out.println("Was broadcaster added? "+added);
}
In this method we created a defaultBroadcaster with the broadcasterID (user id) and assign it the atmosphere resource.

Running our example:

Open a browser and enter the following url:

http://localhost:8888/gwtDemo/gwtDemo.jsp?login=reda&gwt.codesvr=127.0.0.1:9997
This browser will listen to push request.

Send notification:

Make the following post request (I used chrome with Client REST simple extension)
http://localhost:8888/gwtDemo/gwtComet?for=reda&gwt.codesvr=127.0.0.1:9997

You should now see a notification on the client browser.


Hope this helps someone who tries to achieve the same. There may be better ways of doing this. I think I will have to use this approach until someone suggests a better solution.

vendredi 18 mars 2011

Starting with atmosphere framework, GWT and Maven under Eclipse

Months ago i was searching for a push solution to integrate to my web applications and I discovered Atmosphere, a nice framework, developed by nice people.

Latest version of Atmosphere is 0.8. Project is growing fast and development is intensive.

In this post, I want to summarize steps I followed to get atmosphere-gwt-demo working.

I use Eclipse Helios (3.6 SR2), I've already Maven plugin installed (m2eclipse), and google-eclipse-plugin (even if this one will not be used for the purpose of this article)

Download atmosphere project:

git clone https://github.com/jfarcand/atmosphere.git

Wait few minutes for download to complete then go to eclipse and import the entire project as a maven project.

Fix some maven properties:

Go to atmosphere-gwt-demo, we'll be editing the pom.xml. Locate the declaration of gwt-maven-plugin (see the complete configuration) and add
<copyWebapp>true</copyWebapp>

Without this line, maven will not copy files located under src/main/META-INF/ (Most important atmosphere.xml) and the application will not work.

For more information see: http://mojo.codehaus.org/gwt-maven-plugin/debug-mojo.html

Install dependencies:

Now, if you try to run the demo, you will get errors. This happens because you didn't install atmosphere runtime dependencies.

For each error (missing class), find its containing sub-project, right click on it -> run as ... -> maven install

Run the demo:

Right click on atmosphere-gwt-demo -> Run as -> Maven build ... and enter in the goal field: gwt:run

Wait few seconds for the embedded jetty server to start and load your application, then open the hosted mode url into your favorite browser (I use Google Chrome with the GWT plugin installed).

Debug atmosphere-gwt-demo

This post shows how to debug a GWT application with the gwt-maven-plugin.

I just changed the port from 8000 to 8001 because for some reason this port was already in use (I think eclipse uses it)

Add the following line to the gwt-maven-plugin configuration in pom.xml of atmosphere-gwt-demo

<debugPort>8001</debugPort>

Right click on atmosphere-gwt-demo->Run As->Maven build ...
In the following dialog, enter "gwt:debug" as goals, save it.

Click on the dropdown of the debug button on the toolbar, select "Debug Configurations".

In the left panel, find “Remote Java Application”, select it and click the icon for "New launch configuration" (top left corner). Accept defaults, save, and close.

Now, you are ready, start the debug server by running the debug launcher we just created. (the one with goal gwt:debug). When you see "Listening for transport dt_socket at address: 8001" in the console output, run the attach launcher we just created (the remote debugger).

Finally, here is the complete gwt-maven-plugin
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>gwt-maven-plugin</artifactId>
<version>${gwt-version}</version>
<configuration>
<module>${gwtModule}</module>
<gwtVersion>${gwt-version}</gwtVersion>
<runTarget>http://localhost:8888/gwtDemo/gwtDemo.jsp</runTarget>
<noServer>false</noServer>
<sourcesOnPath>true</sourcesOnPath>
<debugPort>8001</debugPort>
<copyWebapp>true</copyWebapp>
</configuration>
<executions>
<execution>
<configuration>
<extraJvmArgs>-Xmx512m</extraJvmArgs>
</configuration>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>