Imagine the following situation when using GWT, RequestFactory and JPA. The server side:
#Entity
public class SuperEmployee implements IsEmployee {...}
#Entity
public class BadEmployee implements IsEmployee {...}
#Entity
public class Supervisor {
List<IsEmployee> employees;
...
}
The client side proxies:
#ProxyFor(value = Supervisor.class, ...)
public interface SupervisorProxy {...}
Now I'd like to have something similar to:
#ProxyFor(value = IsEmployee.class, ...)
public interface EmployeerProxy {...}
Of course this isn't working, but what would be the solution if I'm not interested in the concrete type on client side and just want to work with the interface methods.
I've read the following articles that might be helpful (but maybe I didn't get the point right):
http://www.gwtproject.org/doc/latest/DevGuideRequestFactory.html (section "Transportable types").
Thanks for enlightening my Requestfactory knowledge!
Ralf
GWT does not (yet) support #ProxyFor pointing to interfaces. This will be possible in 2.7 though.
In the mean time, you have to use a base class on the server-side, or a hierarchy of interfaces on the client-side (possibly with #ExtraTypes pointing to the specific interfaces if they're not used directly otherwise)
Related
Situation
I inherited some code from a developer that seems to have used undocumented features of spring.
Specifically, it is related to using #projection functionality without actually annotating the interfaceas such.
A specific example is the following:
public interface DirType extends KeyValInterface {
#Value("#{target.id}") String getId();
#Value("#{target.code}") String getText();
}
This should not work according to the official documentation, but it does.
public interface DirTypeRepository extends JpaRepository<ABDirType, Long> {
List<DirType> findAllSummarizedBy();
}
So the method findAllSummarizedBy() of the respository is actually sending a list of DirType.
And it is using the #Valueannotations to do the mapping, but without a #Projection annotation.
The Entity class is the following:
#Data #Entity #Table(name="dir_type") #AllArgsConstructor #NoArgsConstructor
public static class ABDirType {
private #Id #GeneratedValue Long id;
private String code;
}
Question
Does anyone have any more information about this undocumented feature related to Projections and not annotating the interface as a #Projection ?
Is this possible in all versions or is it a hidden hack that is risky to use?
In Spring Data JPA (which you seem to be using here), you can simply use an interface with appropriate getters as projection targets.
See the documentation about this (esp. "Example 62. Simple Projection").
There does not seem to be a #Projection annotation anywhere in the documentation.
Background: I asked another question (here: Performance degradation after moving to jersey 2) about jersey performance. I also opened an issue in jersey Jira. Apparently, there is a known performance problem with sub-resources in jersey 2.
My data model based on entity A, with several sub-entities. However, the ID of the sub entities is unique, and the server allows to access them directly. For example,
/server/As/{a_id}/Bs/{b_id} == /server/Bs/{b_id}
We're using sub-resources for that, so the same resource (B) is both a spring component and a member of A resource. All the resources are spring beans, so both Bs are the same instance.
Now I'm trying to workaround this problem by not using sub-resources at all. I found out that #Path does not support multiple paths. Is there any idea how to solve it?
I tried the following, does it make sense? Can you offer alternatives? I'm asking because I'll have to do the same trick many times (for entities C, D, E etc. and maybe additional level of resources)
First, I removed B reference from A resource class, then:
public abstract class AbstractB {
#GET
#Path({b_id})
#Produce(MediaType.APPLICATION_JSON)
public Response getB(#PathParam("b_id") String bId) {
...
}
...
}
#Component
#Path ("As/{a_id}/Bs")
public class B extends AbstractB{/* empty */}
#Component
#Path ("Bs")
public class AsB extends AbstractB{/* empty */}
Edit: (by peeskillet) - Link to issue
The Adobe AEM software provides several classes which can take an apache Sling Resource and adapt it to another class like so:
Page page = resource.adaptTo(Page.class);
To use this syntax with classes that you author and control this boils down to simply implementing the Adaptable interface.
However, if you want to enable a Resource to adaptTo your new custom class, is seems that you have to implement the AdapterFactory interface and register it in OSGI.
This is how the Adobe website describes it:
By an AdapterFactory, which can map arbitrary objects.
The objects must still implement the Adaptable interface and must extend SlingAdaptable (which passes the adaptTo call to a central adapter manager).
This allows hooks into the adaptTo mechanism for existing classes, such as Resource.
I have walked through the SlingScriptAdapterFactory code, but ultimately I am not connecting the dots here. Basically I want to do this:
MyClass myClass = Resource.adaptTo(MyClass.class);
Do I create a class that implements AdapterFactory and simply deploy it with the package expecting that Sling will just find it by type or is there more to it?
Here is a little bit better documentation https://sling.apache.org/documentation/the-sling-engine/adapters.html
So you should implement the Adaptable interface, as you already described. Then create a properly annotated AdapterFactory:
#Component
#Service(value=org.apache.sling.api.adapter.AdapterFactory.class)
#Properties({
#Property(name = "adaptables", value = { "org.apache.sling.api.resource.Resource" }),
#Property(name = "adapters", value = { "org.sling.MyClass" })
})
public class MyAdapterFactory implements AdapterFactory{
public <AdapterType> AdapterType getAdapter(final Object adaptable, Class<AdapterType> type){
return new MyClassAdapter(adaptable);
}
}
Note that I've been working on a simpler way to create Sling adapters, by annotating methods with a new #Adapter annotation, as in
#Component
#Service
public class C implements AdapterMethodsProvider {
#Adapter
public CustomerRecord convert(Resource r) { ... }
#Adapter
public Person adaptToPerson(Resource r) { ... }
}
See https://issues.apache.org/jira/browse/SLING-2938 for details, but note that this is not even in the Sling trunk yet, so it will take some time before it's released and available in AEM/CQ.
I would like to use the same code to sort and manipulate objects in client and server sides.
But I am facing a problem since in client we need a proxy interface representing the class of the server.
Is there a way to use the same interface in both?, I know RF has a mechanism to copy bean attributes from the server instance to the client instance when it is sent through the wire.
One way to use the same API is to use interfaces that both your proxies extend and your domain objects implement.
// common interfaces
interface Foo { … }
interface Bar<T extends Foo> {
int getX();
void setX(int x);
// setters need to use generics
List<T> getFoos();
void setFoos(List<T> foos);
// with only a getter, things get easier:
Bar getParent();
}
// domain objects
class RealFoo implements Foo { … }
class RealBar implements Bar<RealFoo> {
int x;
List<RealFoo> foos;
RealBar parent;
#Override
public RealBar getParent() { return parent; }
// other getters and setters
}
// proxy interfaces
#ProxyFor(RealFoo.class)
interface FooProxy extends Foo { … }
#ProxyFor(RealBar.class)
interface BarProxy extends Bar<FooProxy> {
#Override
BarProxy getParent();
// other getters and setters
}
You can then use a Comparator<Foo> or Comparator<Bar> in both client and server side.
I generally only implement traits (aspects, facets, call them the way you like) that way though (HasId, HasLabel, HasPosition, etc.), not complete domain objects' APIs. I can then use HasId to get the key of any object to put them in a map or compare for equality, HasLabel for displays (custom Cells on the client-side, error messages on the server-side that are sent to the client, etc.), HasPosition for sorting, etc.
As Thomas says in his answer, the only way in current GWT to have shared code in client and sever is implementing the same interface in both sides and using it in your shared code.
Since RF copies attributes from the server to the client as you say in your query, in theory we could use the same interface (the proxy one) in both sides (simpler code), setting the #ValueFor value pointing to itself.
Lets see an example:
// Shared interface in client and server sides
#ProxyFor(Foo.class)
interface Foo extends ValueProxy {
String getBar();
}
// Server side implementation
class FooImpl implements Foo {
String getBar(){return "bar";};
}
As information, we use this approach in our product, so as we can sell 2 backend solutions (one is based on GAE and other on couchdb).
The code above works for client code which does not create new values, but if you want to create them, it is enough to define a value locator:
// Say RF which locator to use to create classes in server side
#ProxyFor(value = Foo.class, locator ALocator.class)
interface Foo extends ValueProxy {
}
public class ALocator extends Locator<Foo, String> {
public Foo create(Class<? extends Foo> clazz) {
return new FooImpl();
}
...
}
Unfortunately, RF does not deal with interfaces in the server side see issues: 7509 and 5762.
But, as you can read in the issues comments, there is already a fix for this (pending for review). Hopefully it would be included in a next release of GWT.
In the meanwhile, you can use this approach, just copying the file ResolverServiceLayer.java in your src folder and applying this patch to it.
The point of RequestFactory is that it does not do use the same type. Each request context describes a set of operations to perform when the call gets to the server (create and find things, then apply setters, then run service methods). As calls are described as just proxies to the real thing on the server, you need a 'fake' model object like a EntityProxy or ValueProxy to ensure that the only calls that can be made are getters and setters - and that sometimes, setters are not allows (when an object has been read from the server but before it has been edited).
If your models are simple, i.e. not holding other objects, but only string, date, and primitives, you can have both the entity and the proxy implement the same interface. However, if the model holds sub-objects, then this is more difficult - the only way possible is to leave out those getters and setters. Otherwise, you can't override those methods in the proxy type to specify the proxy version of that nested object.
Consider using RPC isntead if you actually want to reuse the same types on the client and server.
I'm trying to implement a inheritence relationship between JPA entities.
Borrowing the example from:
http://openjpa.apache.org/builds/1.0.2/apache-openjpa-1.0.2/docs/manual/jpa_overview_mapping_discrim.html
#Entity
#Table(name="SUB", schema="CNTRCT")
#DiscriminatorColumn(name="KIND", discriminatorType=DiscriminatorType.INTEGER)
public abstract class Subscription {
...
}
#Entity(name="Lifetime")
#DiscriminatorValue("2")
public class LifetimeSubscription
extends Subscription {
...
}
}
#Entity(name="Trial")
#DiscriminatorValue("3")
public class TrialSubscription
extends Subscription {
...
}
What I need to be able to do is have an additional entity that catches the rest, something like:
#Entity(name="WildCard")
#DiscriminatorValue(^[23])
public class WildSubscription
extends Subscription {
...
}
Where if it does not match LifetimeSubscription or TrialSubscription it will match WildSubscription.
It actually makes a bit more sense if you think of it where the wild is the superclass, and if there is not a more concrete implementation that fits, use the superclass.
Anyone know of a method of doing this?
Thanks!
The JPA API allows only plain values here, and for a reason: discriminator values are mapped to SQL WHERE:
SELECT ... WHERE kind = 1
If you could specify regular expressions here, it wouldn't be transferable to SQL, as it does not support such constructs.