com.google.gwt.requestfactory.shared.impl.ProxySerializerImpl Maven / Gradle / Ivy
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.requestfactory.shared.impl;
import com.google.gwt.autobean.shared.AutoBean;
import com.google.gwt.autobean.shared.AutoBeanCodex;
import com.google.gwt.autobean.shared.AutoBeanFactory;
import com.google.gwt.autobean.shared.AutoBeanUtils;
import com.google.gwt.autobean.shared.AutoBeanVisitor;
import com.google.gwt.autobean.shared.Splittable;
import com.google.gwt.requestfactory.shared.BaseProxy;
import com.google.gwt.requestfactory.shared.EntityProxy;
import com.google.gwt.requestfactory.shared.EntityProxyId;
import com.google.gwt.requestfactory.shared.ProxySerializer;
import com.google.gwt.requestfactory.shared.ProxyStore;
import com.google.gwt.requestfactory.shared.messages.IdMessage;
import com.google.gwt.requestfactory.shared.messages.IdMessage.Strength;
import com.google.gwt.requestfactory.shared.messages.OperationMessage;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* The default implementation of ProxySerializer.
*
* RequestFactory has moved to
* com.google.web.bindery.requestfactory
. This package will be
* removed in a future version of GWT.
*/
@Deprecated
class ProxySerializerImpl extends AbstractRequestContext implements
ProxySerializer {
/**
* Used internally to unwind the stack if data cannot be found in the backing
* store.
*/
private static class NoDataException extends RuntimeException {
}
private final ProxyStore store;
/**
* If the user wants to serialize a proxy with a non-persistent id (including
* ValueProxy), we'll assign a synthetic id that is local to the store being
* used.
*/
private final Map, SimpleProxyId>> syntheticIds = new HashMap, SimpleProxyId>>();
/**
* The ids of proxies whose content has been reloaded.
*/
private final Set> restored = new HashSet>();
private final Map, AutoBean>> serialized = new HashMap, AutoBean>>();
public ProxySerializerImpl(AbstractRequestFactory factory, ProxyStore store) {
super(factory, Dialect.STANDARD);
this.store = store;
}
public T deserialize(Class proxyType, String key) {
// Fast exit to prevent getOperation from throwing an exception
if (store.get(key) == null) {
return null;
}
OperationMessage op = getOperation(proxyType, key);
@SuppressWarnings("unchecked")
SimpleProxyId id = (SimpleProxyId) getId(op);
return doDeserialize(id);
}
public T deserialize(EntityProxyId id) {
return doDeserialize((SimpleEntityProxyId) id);
}
/**
* Replace non-persistent ids with store-local ids.
*/
@Override
public Splittable getSerializedProxyId(SimpleProxyId> stableId) {
return super.getSerializedProxyId(serializedId(stableId));
}
public String serialize(BaseProxy rootObject) {
final AutoBean extends BaseProxy> root = AutoBeanUtils.getAutoBean(rootObject);
if (root == null) {
// Unexpected, some kind of foreign implementation of the BaseProxy?
throw new IllegalArgumentException();
}
final SimpleProxyId> id = serializedId(BaseProxyCategory.stableId(root));
// Only persistent and synthetic ids expected
assert !id.isEphemeral() : "Unexpected ephemeral id " + id.toString();
/*
* Don't repeatedly serialize the same proxy, unless we're looking at a
* mutable instance.
*/
AutoBean> previous = serialized.get(id);
if (previous == null || !previous.isFrozen()) {
serialized.put(id, root);
serializeOneProxy(id, root);
root.accept(new AutoBeanVisitor() {
@Override
public void endVisit(AutoBean> bean, Context ctx) {
// Avoid unnecessary method call
if (bean == root) {
return;
}
if (isEntityType(bean.getType()) || isValueType(bean.getType())) {
serialize((BaseProxy) bean.as());
}
}
@Override
public void endVisitCollectionProperty(String propertyName,
AutoBean> value, CollectionPropertyContext ctx) {
if (value == null) {
return;
}
if (isEntityType(ctx.getElementType())
|| isValueType(ctx.getElementType())) {
for (Object o : value.as()) {
serialize((BaseProxy) o);
}
}
}
});
}
return getRequestFactory().getHistoryToken(id);
}
@Override
protected AutoBeanFactory getAutoBeanFactory() {
return getRequestFactory().getAutoBeanFactory();
}
@Override
SimpleProxyId getId(IdMessage op) {
if (Strength.SYNTHETIC.equals(op.getStrength())) {
return getRequestFactory().allocateSyntheticId(
getRequestFactory().getTypeFromToken(op.getTypeToken()),
op.getSyntheticId());
}
return super.getId(op);
}
@Override
AutoBean getProxyForReturnPayloadGraph(
SimpleProxyId id) {
AutoBean toReturn = super.getProxyForReturnPayloadGraph(id);
if (restored.add(id)) {
/*
* If we haven't seen the id before, use the data in the OperationMessage
* to repopulate the properties of the canonical bean for this id.
*/
OperationMessage op = getOperation(id.getProxyClass(),
getRequestFactory().getHistoryToken(id));
this.processReturnOperation(id, op);
toReturn.setTag(Constants.STABLE_ID, super.getId(op));
}
return toReturn;
}
/**
* Reset all temporary state.
*/
private void clear() {
syntheticIds.clear();
restored.clear();
serialized.clear();
}
private T doDeserialize(SimpleProxyId id) {
try {
return getProxyForReturnPayloadGraph(id).as();
} catch (NoDataException e) {
return null;
} finally {
clear();
}
}
/**
* Load the OperationMessage containing the object state from the backing
* store.
*/
private OperationMessage getOperation(Class proxyType, String key) {
Splittable data = store.get(key);
if (data == null) {
throw new NoDataException();
}
OperationMessage op = AutoBeanCodex.decode(MessageFactoryHolder.FACTORY,
OperationMessage.class, data).as();
return op;
}
/**
* Convert any non-persistent ids into store-local synthetic ids.
*/
private SimpleProxyId serializedId(
SimpleProxyId stableId) {
assert !stableId.isSynthetic();
if (stableId.isEphemeral()) {
@SuppressWarnings("unchecked")
SimpleProxyId syntheticId = (SimpleProxyId) syntheticIds.get(stableId);
if (syntheticId == null) {
int nextId = store.nextId();
assert nextId >= 0 : "ProxyStore.nextId() returned a negative number "
+ nextId;
syntheticId = getRequestFactory().allocateSyntheticId(
stableId.getProxyClass(), nextId + 1);
syntheticIds.put(stableId, syntheticId);
}
return syntheticId;
}
return stableId;
}
private void serializeOneProxy(SimpleProxyId> idForSerialization,
AutoBean extends BaseProxy> bean) {
AutoBean op = makeOperationMessage(
serializedId(BaseProxyCategory.stableId(bean)), bean, false);
store.put(getRequestFactory().getHistoryToken(idForSerialization),
AutoBeanCodex.encode(op));
}
}