Question

In my discovery of tynamo and resteasy integration, I'm facing an issue on saving datas, that I partially solved (so I will partially share it ;) ). User guide is here : http://docs.codehaus.org/display/TYNAMO/tapestry-resteasy+guide

I wrote a rest service that allow "users" registration in my system users :

package com.myorg.mobile.pushup.rest;
//[...]

/**
 * bla blah obfuscated pretty comments.
 * 
 * @author jrrevy
 * 
 */
@Path("/user")
public class UserResource {

    private Session session;

    /**
     * Constructeur du service
     * 
     * @param session
     *            Hibernate session
     */
    public UserResource(Session session) {
        this.session = session;
    }   

    /**
     * Lecture de tous les utilisateurs
     * 
     * @return une liste des utilisateurs existants
     */
    @GET
    @Produces("application/json")
    public List<User> getAllDomains() {
        return session.createCriteria(User.class).list();
    }

    /**
     * Create User.
     * 
     * @param user
     *            user to create
     * @return a Web Response
     */
    @POST
    @PUT
    @Produces({ "application/xml", "application/json" })
    public Response createOrUpdate(User user) {
        session.saveOrUpdate(user);
        return Response.ok().build();
    }

Access to database works well (I get my users back, an saw INSERT INTO command into the logs), but this service never persists anything. It seems that the transaction is never commited.

I'm using 0.3.0 version of tapestry-model-hibernate and tapestry-resteasy :

<dependencies>

    <dependency>
        <groupId>org.tynamo</groupId>
        <artifactId>tapestry-model-hibernate</artifactId>
        <version>0.3.0</version>
    </dependency>


    <dependency>
        <groupId>org.tynamo</groupId>
        <artifactId>tapestry-resteasy</artifactId>
        <version>0.3.0</version>
    </dependency>

</dependencies>

See my answer above, but please tell me if you found a better way.

Was it helpful?

Solution

According to this thread : http://osdir.com/ml/java-tynamo-user/2012-04/msg00005.html, this is merely a COMMIT issue :).

In standard context, the transaction is deal by the Page that called the service. In REST approach, this job should be done by the service itself.

The idea is, indeed, to change your autodiscovered REST service into a real tapestry IOC one.

1 - Move it away from autodiscovery

Move it into service package (or anyone that allow Tapestry binding and not in rest autodiscovery package) :

2 - Extract Interface

Extract the interface, and put annotations on it + CommitAfter where you want to deal with transaction .

package com.myorg.mobile.pushup.services.user;

@Path("/user")
public interface IUserResource {

    /**
     * Lecture de tous les utilisateurs
     * 
     * @return une List des utilisateurs existants
     */
    @GET
    @Produces("application/json")
    List<User> getAllDomains();


    /**
     * Méthode d'enregistrement / MAJ d'un utilisateur.
     * 
     * @param user
     *            l'utilisateur à créer
     * @return
     */
    @POST
    @PUT
    @Produces({ "application/xml", "application/json" })
    @Consumes({ "application/xml", "application/json" })
    @CommitAfter
    Response registerOrUpdate(User user);
}

Delete annotation on implementation

package com.myorg.mobile.pushup.services.user.impl;

public class HibernateUserResourceImpl implements IUserResource {
    /* (non-Javadoc)
     */

    public List<User> getAllDomains() {

        return session.createCriteria(User.class).list();
    }

    /* (non-Javadoc)
     */

    Response registerOrUpdate(User user);
        session.saveOrUpdate(user);
        return Response.ok().build();
    }

3 - Contribute it as a singleton Resource

public class AppModule {
    public static void bind(ServiceBinder binder) {
        binder.bind(IUserResource.class, HibernateUserResourceImpl.class);
    }

    /**
     * Contributions to the RESTeasy main Application, insert all your RESTeasy
     * singletons services here.
     */
    @Contribute(javax.ws.rs.core.Application.class)
    public static void configureRestResources(Configuration<Object> singletons,
            IUserResource userResource) {
        singletons.add(userResource);
    }

}

4 - Advise annotate methods that they are transactionals

public class AppModule {

    @Match("*Resource*")
    public static void adviseTransactionally(
            HibernateTransactionAdvisor advisor, MethodAdviceReceiver receiver) {

        advisor.addTransactionCommitAdvice(receiver);
    }

You may add another dependencies if you'd prefer to use JPATransactionAdvision instead of Hibernate one :

    <dependency>
        <groupId>org.tynamo</groupId>
        <artifactId>tapestry-jpa-core</artifactId>
        <version>2.0.1</version>
    </dependency>

5 - Conclusion

This solution is working for me, but as far as I understand it, this is actually the only way to do it.

Alejandro > If you read those lines, please tell me if there is another (shortest) way, of I'm right :)

OTHER TIPS

You can use HibernateSessionManager Implementation (given inside tapestry-hibernate dependency) to get session and commit the data instantly as below,

@Inject 
HibernateSessionManager hibernateSessionManager;

public  void saveOrUpdate(MyEntity instance) {
    Session session = hibernateSessionManager.getSession();
    session.saveOrUpdate(instance);
    hibernateSessionManager.commit();
    session.flush();
}

Here you can't close the session. Because session is shared, if you try to close the session you may got exception like "hibernate session is closed".

Also transaction is shared here, so you can only commit the transaction using commit() function given in hibernateSessionManager without knowing the current transaction.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top