jan
jan

Reputation: 4103

GWT request factory - how to get locator working

I have a working "request factory" example and i want to refactor it, so that i can move the generic methods like "persist()" and "remove()" out of the domain object into a generic locator. Currently i have the following (working) code:

A generic super class that holds the id and the version for all domain objects:

@MappedSuperclass  
public class EntityBase {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Version
    @Column(name = "version")
    private Integer version;

    // setter & getter

}

A domain object. It has the persist() and remove()-methods, which i want to refactore out of the class:

@Entity
@Table(name = "article")
public class Article extends EntityBase{

    public static Article findArticle(Long id) {
        //find article
    }


    public void persist() {
        // persist
    }

    public void remove() {
        // remove
    }

}

A proxy object for the domain object:

@ProxyFor(value = Article.class)
public interface ArticleProxy extends EntityProxy {

    // some getter and setters

}

The request object for my domain object:

@Service(value = Article.class)
public interface ArticleRequest extends RequestContext {

    Request<ArticleProxy> findArticle(Long id);

    InstanceRequest<ArticleProxy, Void> persist();

    InstanceRequest<ArticleProxy, Void> remove();
}

My request factory:

public interface MyRequestFactory extends RequestFactory {

  ArticleRequest articleRequest();

}

---------------------------------------

Now my refactored code that is not working anymore:

I removed the persist() and remove()-method out of my domain object:

@Entity
@Table(name = "article")
public class Article extends EntityBase{

    public static Article findArticle(Long id) {
        //find article
    }

}

I created my locator like this and added the methods "remove()" and "persist()" here (alongside the other default methods):

public class EntityLocator extends Locator<EntityBase, Long> {

    @Override
    public EntityBase create(Class<? extends EntityBase> clazz) {
        try {  
            return clazz.newInstance();  
        } catch (InstantiationException e) {  
            throw new RuntimeException(e);  
        } catch (IllegalAccessException e) {  
            throw new RuntimeException(e);  
        }  
    }

    @Override
    public EntityBase find(Class<? extends EntityBase> clazz, Long id) {
        return null;
    }


    @Override
    public Class<EntityBase> getDomainType() {
        return null;
    }

    @Override
    public Long getId(EntityBase domainObject) {
        return null;
    }

    @Override
    public Class<Long> getIdType() {
        return null;
    }

    @Override
    public Object getVersion(EntityBase domainObject) {
        return null;
    }

    public void persist(EntityBase domainObject){
        // persist something
    }

    public void remove(EntityBase domainObject){
        // remove
    }

}

My proxy object is linked to the locator (locator=EntityLocator.class):

@ProxyFor(value = Article.class, locator=EntityLocator.class)
public interface ArticleProxy extends EntityProxy {

    // getter and setters here

}

My new Request object looks like this. I made the "InstanceRequests" to "Requests", changed return types and parameter according to my new methods in the locator:

@Service(value = Article.class)
public interface ArticleRequest extends RequestContext {

    Request<ArticleProxy> findArticle(Long id);

    Request<Void> persist(ArticleProxy article);

    Request<Void> remove(ArticleProxy article);

}

But now i get the error "Could not find domain method similar to java.lang.Void persist()" for the persist() and remove()-method. Why doesn't the lookup in the EntityLocator work? Do i need a ServiceLocator? I did not fully understand the google tutorial and the linked example is not available anymore.

Upvotes: 1

Views: 1669

Answers (4)

emrvb
emrvb

Reputation: 21

I had the same question as you. The guide on GWTProject.org (http://www.gwtproject.org/doc/latest/DevGuideRequestFactory.html) is not very clear on how to correctly implement this, although it is written between the lines.

The following tutorial made the solution clear to me: http://cleancodematters.com/2011/06/04/tutorial-gwt-request-factory-part-i/

For me the use of the term DAO obfuscated things. I'm not going to use the DAO pattern. That's what my transparent persistence layer is for. However, the use of the Locator requires an extra class to put the persist, remove and findX methods in. They call it a Data Access Object (which it is, actually), I'd rather call it the Manager.

tl;dr

The methods you're trying to put in the Locator don't go there. You need an extra class (call it a DAO or a Manager).

Use the DAO/Manager as service in your RequestContext

Upvotes: 1

Nick V
Nick V

Reputation: 1671

Your interface ArticleRequest isn't configured properly. You need correct it like this.

@Service(value = SentenceServiceImpl.class, locator = SpringServiceLocator.class)
public interface SentenceServiceRequest extends RequestContext {

    Request<List<SentenceProxy>> getSentences();

    Request<Void> saveOrUpdate(SentenceProxy sentence);

}

Locator:

public class SpringServiceLocator implements ServiceLocator {

    public Object getInstance(Class<?> clazz) {
        ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(RequestFactoryServlet.getThreadLocalServletContext());
        return context.getBean(clazz);
    }

}

Upvotes: 0

Although it makes sense that persist() and remove() were in the Locator, so as the entity was completely agnostic about the persistence layer, this is not supported by current RF api. As consequence you have to deal with that adding those methods to your BaseEntity and figuring out a way to call the persist method in your locator.

I think you could open a gwt issue requiring this feature though.

Another way to avoid having certain methods in your entities, is to use ValueProxy insteadof EntityProxy, but in this case you have to provide methods to save/delete those objects from the client.

Upvotes: 0

Joe Bandenburg
Joe Bandenburg

Reputation: 13

I don't think you can place the persist and remove methods in the locator. The documentation doesn't suggest you can add arbitrary methods to the locator interface and reference them from the client. If you just want to avoid duplicating the persist and remove methods in every entity class then you can put them in your EntityBase class. I've done this and it works nicely.

If you also want to avoid repeating the functions in each of your request interfaces, you can make a generic base class Request like so:

@SkipInterfaceValidation
public interface BaseEntityRequest<P extends EntityProxy> extends RequestContext {
    InstanceRequest<P, Void> persist();
    InstanceRequest<P, Void> remove();
}

and use it like so:

public interface ArticleRequest extends BaseEntityRequest<ArticleRequest> {
    ...
}

Upvotes: 0

Related Questions