|
AutoBean
Structure, not boilerplate
GWT AutoBean frameworkThe AutoBean framework provides automatically-generated implementations of bean-like interfaces and a low-level serialization mechanism for those interfaces. AutoBeans can be used in both client and server code to improve code re-use. For example, the Requestfactory system uses AutoBeans extensively in both the client and server code. This document describes the state of AutoBeans as found in the GWT 2.1.1 release onwards.
Goals
Non-goals
If higher-order model semantics are needed, the non-goals for AutoBeans are key features of the RequestFactory framework. Quickstart// Declare any bean-like interface with matching getters and setters, no base type is necessary
interface Person {
Address getAddress();
String getName();
void setName(String name);
void setAddress(Address a);
}
interface Address {
// Other properties, as above
}
// Declare the factory type
interface MyFactory extends AutoBeanFactory {
AutoBean<Address> address();
AutoBean<Person> person();
}
class DoSomething() {
// Instantiate the factory
MyFactory factory = GWT.create(MyFactory.class);
// In non-GWT code, use AutoBeanFactorySource.create(MyFactory.class);
Person makePerson() {
// Construct the AutoBean
AutoBean<Person> person = factory.person();
// Return the Person interface shim
return person.as();
}
String serializeToJson(Person person) {
// Retrieve the AutoBean controller
AutoBean<Person> bean = AutoBeanUtils.getAutoBean(person);
return AutoBeanCodex.encode(bean).getPayload();
}
Person deserializeFromJson(String json) {
AutoBean<Person> bean = AutoBeanCodex.decode(factory, Person.class, json);
return bean.as();
}
}Property typesThe following types may be used to compose AutoBean interfaces:
AutoBeanAn AutoBean must be parameterized with an interface type (e.g. AutoBean<Person>). This interface type may have any type hierarchy and need not extend any particular type in order to be usable with AutoBeans. A distinction is made as to whether or not the target interface is "simple." A simple interface satisfies the following properties:
A simple AutoBean can be constructed by the AutoBeanFactory without providing a delegate instance. If a reference interface is returned from a method in a target interface, that instance will be automatically wrapped by an AutoBean instance. This behavior can be disabled by placing a @NoWrap annotation on the AutoBeanFactory. accept()The AutoBean controller provides a visitor API to allow the properties of an AutoBean to be examined by code that has no prior knowledge of the interface being wrapped. as()The AutoBean acts as a controller for a shim object that implements the interface with which the AutoBean is parameterized. For instance, in order to get the Person interface for an AutoBean<Person> it is necessary to call the as() method. The reason for this level of indirection is to avoid any potential for method signature conflicts if the AutoBean were to also implement its target interface. clone()An AutoBean and the property values stored within it can be cloned. The clone() method has a boolean parameter that will trigger a deep or a shallow copy. Any tag values associated with the AutoBean will not be cloned. AutoBeans that wrap a delegate object cannot be cloned. getTag() / setTag()Arbitrary metadata of any type can be associated with an AutoBean by using the AutoBean as a map-like object. The tag values do not participate in cloning or serialization operations. isFrozen() / setFrozen()Property mutations can be disabled by calling setFrozen(). Any attempts to call a setter on a frozen AutoBean will result in an IllegalStateException. isWrapper() / unwrap()If the factory method used to instantiate the AutoBean provided a delegate object, the AutoBean can be detached by calling the unwrap() object. The isWrapper() method will indicate AutoBeanFactoryInstead of requiring a call to GWT.create() for every instance of an AutoBean, only the AutoBeanFactory must be constructed with a call to GWT.create() or AutoBeanFactorySource.create() (in 2.1.1 and 2.2, AutoBeanMagicSource was named AutoBeanFactoryMagic). This allows the AutoBeanFactory to be provided to consuming code by any dependency-injection pattern desired. Methods in an AutoBeanFactory interface must return AutoBean<Foo>, where Foo is any interface type compatible with AutoBeans. The methods may optionally declare a single parameter of type Foo which allows construction of an AutoBean around an existing object. interface MyFactory extends AutoBeanFactory {
// Factory method for a simple AutoBean
AutoBean<Person> person();
// Factory method for a non-simple type or to wrap an existing instance
AutoBean<Person> person(Person toWrap);
}create()The create() method accepts a Class object of any interface type reachable from the AutoBeanFactory interface. An optional parameter allows a delegate object to be supplied that will be wrapped by the returned AutoBean. AutoBeanCodexThe AutoBeanCodex provides general-purpose encoding and decoding of AutoBeans into a JSON-formatted payload. decode()This method accepts an AutoBeanFactory, a Class object representing the top-level AutoBean interface type to be returned, and a JSON-formatted payload. The provided AutoBeanFactory must be capable of producing AutoBeans for all interface types reachable from the provided interface. encode()This method accepts an AutoBean and returns a Splittable representing a JSON payload that contains the properties of the AutoBean and its associated object graph. SplittableThe Splittable type is an abstraction around the low-level wire format and library used to manipulate the wire format. For example, in the client-side code Splittable is a JavaScriptObject, while on the server it is backed by the org.json library. The interface offers methods that allow the underlying data model to be queried. Whenever the AutoBeanCodex encounters a Splittable property or collection of Splittable, the contents returned by the Splittable.getPayload() method will be injected directly into the wire format. The Splittable type allows message objects backed by different AutoBeanFactory types to combined in a single payload, since the Splittable must be explicitly decoded via AutoBeanCodex.decode(). AutoBeanVisitorAutoBeanVisitor is a concrete, no-op, base type that is intended to be extended by developers that wish to write reflection-like code over an AutoBean's target interface. visit() / endVisit()Regardless of the reference structure of an AutoBean graph, a visitor will visit any given AutoBean exactly once. Users of the AutoBeanVisitor should not need to implement cycle-detection themselves. As of GWT 2.1.1, the Context interface is empty and exists to allow for future expansion. visitReferenceProperty() / visitValueProperty()The property visitation methods in an AutoBeanVisitor type will receive a PropertyContext object that allows the value of the property to be mutated as well as providing type information about the field. Calling the canSet() method before calling set() promotes good code hygiene. visitCollectionProperty() / visitMapProperty()These visitation methods behave similarly to visitReferenceProperty() however the PropertyContext passed into these methods is specialized to provide the parameterization of the Collection or Map object. AutoBeanUtilsdiff()Performs a shallow comparison between the properties in two AutoBeans and returns a map of the properties that are not equal to one another. The beans do not need to be of the same interface type, which allows for a degree of duck-typing. getAllProperties()Creates a shallow copy of the properties in the AutoBean. Modifying the structure of the returned map will not have any effect on the state of the AutoBean. The reference values in the map are not cloned, but are the same instances held by the AutoBean's properties. JSON structuresThe AutoBean framework can be used as a JSON interoperability layer to provide a Java typesystem wrapper around an existing JSON api or to create JSON payloads to interact with a remote service. This is accomplished by designing the Java APIs according to the JSON schema. The @PropertyName annotation can be applied to getters and setters where the Java naming convention does not align with the JSON schema. Generally speaking, the serialized form of an object emitted by AutoBeanCodex mirrors the interface declaration. For instance, the example Person interface described in the quckstart section of this document might be serialized as: // Whitespace added for clarity
{ "name" : "John Doe", "address" : { "street" : "1234 Maple St", "city" : "Nowhere" } }List and Set properties are encoded as JSON lists. For example, a List<Person> would be encoded as: [ { "name" : "John Doe" } , { "name" : "Jim Smith" } ]Maps are serialized in two forms based on wether or not the key type is a value or reference type. Value maps are encoded as a typical JSON object. For example, a Map<Integer, Foo> would be encoded as { "1" : { "property" : "value"}, "55" : { "property" : "value" } }A map that uses a reference object as a key will instead be encoded as a list of two lists. This allows object-identity maps that contain keys with identical serialized froms to be deserialized correctly. For example, a Map<Person, Address> would be encoded as: [ [ { "name" : "John Doe" } , { "name" : "Jim Smith" } ] , [ { "street" : "1234 Maple Ave" }, { "street" : "5678 Fair Oaks Lane" } ] ]Java enum values are written out as the string name of the enum value. This can be overridden by applying the PropertyName annotation to the enum field declaration. The use of names instead of ordinal values will allow the payloads to be robust against endpoint schema skew. CategoriesPure bean interfaces only go so far to producing a useful system. For example, the EntityProxy type used by RequestFactory is an AutoBean interface, save for the addition of the stableId() method. An AutoBeanFactory can produce non-wrapper (aka "simple") instances of a non-simple interface if an implementation of any non-property interface is provided by a category. interface Person {
String getName();
void setName(String name);
boolean marry(Person spouse);
}
@Category(PersonCategory.class)
interface MyFactory {
// Would be illegal without a category providing an implementation of marry(AutoBean<Person> person, Person spouse)
AutoBean<Person> person();
}
class PersonCategory {
public static boolean marry(AutoBean<Person> instance, Person spouse) {
return new Marriage(instance.as(), spouse).accepted();
}
}For any non-property method, the category must declare a public, static method which has an additional 0-th parameter which accepts the AutoBean backing the instance. Another example from RequestFactory demonstrating the implementation of the stableId() method: class EntityProxyCategory {
EntityProxyId<?> stableId(AutoBean<EntityProxy> instance) {
return (EntityProxyId<?>) instance.getTag("stableId");
}The @Category annotation may specify more than one category type. The first method in the first category whose name matches the non-property method that is type-assignable will be selected. The parameterization of the 0-th parameter AutoBean is examined when making this decision. InterceptorsA category implementation may additionally declare an interceptor method to examine and possible replace the return values of all non-void methods in the target interface: public static <T> T __intercept(AutoBean<?> bean, T returnValue) {
// Do stuff
return maybeAlteredReturnValue;
}RequestFactory uses this to make EntityProxy objects returned from an editable object editable. FAQ |
is it possible to use 2.1.1 RequestFactory?/AutoBean to convert POJO to JSON and viceversa when interacting with a RESTful Web Service ?
the common practice with GWT client-server communication is to have packages: client, shared, server, configuring web.xml
however, in-order to be truly loosely coupled, I am thinking of developing the Server side of my app, as a REST WebService?. completely separate from the client. sending JSON encoded data to the Server, and reading JSON.
only using a common .jar file on both containing Entity POJOs.
the problem on GWT client side is a clean way of Converting JSON to POJO back and forth.
I was wondering if the new AutoBean/RequestFactory?/EntityProxy? can help in the situation I described ?
Can i still use "Editor" if I opt for JSON/REST solution above ? What do you think of this approach ? do you think it may result in performance problems ?
I am going to write both the client and servers in Java, and have access to the code. but my main concern is clean separation, complete isolation and loose coupling.
I would like to know what are the down-sides to this, and if the new 2.1.1 features can help ?
these Architectural decisions are about tradeoff, I would like to know what I am going to lose if I chose the approach I described above.
Thank You
could you please provide a very basic HelloAutoBean? sample in the trunk ?
I have been struggling putting code fragments in this wiki together, but still couldn't get a basic sample to work.
from the server side of things, how does AutoBean relate to Jackson JSON processor ?
Rather than manually declaring interfaces, why not do just
class Person {
}and then have some internal code generators for required AutoBean structures?
See also http://code.google.com/p/google-gson/ which is very easy to use (no boilerplate code), but which doesn't work on GWT due to lack of reflection. I would love AutoBean to work in the same way as gson.
There are some mistakes in the sample code:
Would have been nice to have a full basic working sample, rather than some code made up in a text editor...
I have the same question as mr autobeanvonautobahn. The current GWT RPC mechanism is a nightmare for interoperability and also difficult to stress test with tools like JMeter. We want the ability to reuse the RPC calls through a JSON API, with not too much handcoding.
In our product we do not care too much about the potential size difference between RPC and JSON. Performance is still important, but RPC was never very performant (stackoverflow, slow javascript warnings).
So I am thinking about writing a generator that combines the Autobean API with Sync/Async interfaces as used by RPC) ... although I have the impression that comes very close to the RequestFactory? ... but the docs state give me the impression that it is not designed for general rpc method calling, but more for handling JPA,Hibernate stuff.
RequestFactory? is not designed for an "RPC style" use, but using only ValueProxy? I believe you could do it anyway (it could also be a first step in migrating from GWT-RPC to RF for "CRUD oriented" apps)
Boilerplate, yes boilerplate
What are those interfaces, factories and categories if not boilerplate?! Have you ever looked at Gson?!
AutoBean is likely to be good work out for easy (de)serialization. But why can't it support non-object types? E.g. List<Integer>, List<String>, String which could be serialized as [1,2,3], ["one","two","three"] and simple text correspondingly. Is it planned in further?
Is there a build configuration change I have to do for using AutoBean API? I'm using gwt-maven-plugin and I see this error when I try to compile my GWT module
In your project.gwt.xml :
<inherits name="com.google.web.bindery.autobean.AutoBean"/>
should fix the issue
Why can't I convert an object that a Map<String,Object>? it throws a null pointer exception and I'm not sure why
'Object' type can't be (de)encoded via AutoBean framework. You should read this section again http://code.google.com/p/google-web-toolkit/wiki/AutoBean#Property_types
I have another problem and I hope you guys could help me out. I have an interface named BaseValue?
void setValue(String value); void setValues(List<BaseValue> value); String getValue(); List<BaseValue> getValues();
and utils methods such as
int getValueAsInt();
As I have understood, I need to create a class to implement the behavior of such method. public class Value{
}and the factory interface should have @Category(Value.class)
but when I do the folowing I can't get it to work AutoBean<BaseFormData> bean = AutoBeanCodex?.decode(
BaseFormData? data = bean.as(); int id = data.getRequest().get( "id" ).getValueAsString(); id is 0. On the other hand data.getRequest().get( "id" ).getValue() returns the correct value Could anyone help me on this?Thanks a lot guys
Regarding Categories:
This is probably loosely implied by the definition of "simple" autobean interfaces, but it may be worth noting that category methods do not seem to work if they begin with the phrase "get", as it seems like it causes the decoder to try and find the matching JSON entity instead of using an implemented category method.
Is there any way to add logic to AutoBeans??
I don't see a way of overriding accessors. If that is not possible, why bother with them in the first place?
Is there a way to specify order of properties in serialized string? For example:
interface JsonRpcRequest { String getVersion(); void setVersion(String version); String getMethod(); void setMethod(String method); List<String> getParams(); void setParams(List<String> params); }AutoBeanCodex?.encode(bean).getPayload() serializes using sort order by property name:
{"method":"method1","params":["param1"],"version":"1.0"}but what I want is this:
{"version":"1.0", "method":"method1","params":["param1"]}Any ideas how to achieve that?
@guus: You can add methods to an AutoBean by using Categories, see allMethods() of http://code.google.com/p/google-apis-explorer/source/browse/trunk/src/com/google/api/explorer/client/base/ApiService.java for example.
@Ognjen.Fabris: I believe the order of JSON keys is designed not to matter, so I don't think that AutoBeans? give you any control over how they are serialized. For what purpose are you trying to serialize in that order? It's possible there's a better way to solve your problem.
@jasonhall: You're right! It seems that I was getting timeout during first test with AutoBean request. When I tried with correct order it worked, and assumed that server side takes order into account. But now works in both cases. Thanks!
Is it possible to define such interface or other class which would deserialize from string containing array e.g. "["abc","def"]", without property name for the array itself? example:
interface arraySample { List<String> getParams(); void setParams(List<String>);this interface can be generated from: "{"params":["abc","def"]}". Is there a way to decode List<String> directly from "["abc","def"]", without property name?
I'm not sure what you're asking here. If you're asking about decoding the object you described above without having to name your field getParams(), you can do that using the @PropertyName? annotation, like so:
interface MyObj? {
}If you want to decode the string "{'params':['a', 'b']}" directly into a list, you can't do that. You'll need to define the object then get the field like you described above.
I don't believe there's a way to decode the string "['a', 'b']" directly to a List. At least not yet?
How to use it with list of custom Objects. I tried several approaches. Is this supported?
This is supported, yes.
interface Container { List<Item> getItems(); } interface Item { String getFoo(); }This will (de)serialize to:
{ "items": [ { "foo": "bar" }, { "foo": "baz" }, ... ] }Jason, could you please update the Quickstart example?
There's a comment that states:
Please update it since the class AutoBeanFactoryMagic? is deprecated in gwt 2.3. and removed in 2.4 -> We should use the Class AutoBeanFactorySource?.
Hi jasonhall. I tried, but it says
Rebinding JsonAutoBeanFactory?
ERROR? Deferred binding failed for 'JsonAutoBeanFactory?'; expect subsequent failures All clients connected (Limiting future permutations to: gecko1_8)My factory looks like:
and the interface
public interface DataJsonContainer? {
}public interface DataJsonContainer? {
}Is the second interface supposed to be named DataJsonObject??
nope, it is DataJsonObject? instead of DataJsonContainer?, sorry for that
Hei guys! Can you update documentation about the AutoBeanFactoryMagic? class? It is deprecated now, but AutoBeanFactorySource? marked as "This is experimental, unsupported code" (ver. 2.4.0). What is the best way to create factory on the server side? Thank you!
Hi Jason,
Is it possible to serialize JavaScriptObject? using AutoBean? Or how to create Splittable from JavaScriptObject??
Hi guys,
Why am I getting the following:
Loading inherited module 'com.google.web.bindery.autobean.AutoBean'
I have added <inherits name="com.google.web.bindery.autobean.AutoBean"/> in my projects xml and added gwt-servlet-deps.jar to my class path
Help appreciated
Any idea why is below exception thrown? Message says Expecting indexed data. Stack trace is pasted below. Help would be appreciated.
java.lang.AssertionError??: Expecting indexed data ERROR? at com.google.web.bindery.autobean.shared.impl.SplittableList??.<init>(SplittableList??.java:64) ERROR? at com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl??$CollectionCoder??.decode(AutoBeanCodexImpl??.java:144) ERROR? at com.google.web.bindery.autobean.shared.impl.AbstractAutoBean??.getOrReify(AbstractAutoBean??.java:241) ERROR? at