blob: 3682f1e3a02bb608a6df4d1903da4b1457f45a27 [file] [log] [blame]
/*
* 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.server;
import com.google.gwt.autobean.server.AutoBeanFactoryMagic;
import com.google.gwt.autobean.shared.AutoBean;
import com.google.gwt.autobean.shared.AutoBeanCodex;
import com.google.gwt.autobean.shared.Splittable;
import com.google.gwt.autobean.shared.ValueCodex;
import com.google.gwt.autobean.shared.impl.StringQuoter;
import com.google.gwt.requestfactory.server.SimpleRequestProcessor.IdToEntityMap;
import com.google.gwt.requestfactory.shared.BaseProxy;
import com.google.gwt.requestfactory.shared.EntityProxy;
import com.google.gwt.requestfactory.shared.ValueProxy;
import com.google.gwt.requestfactory.shared.impl.Constants;
import com.google.gwt.requestfactory.shared.impl.EntityCodex;
import com.google.gwt.requestfactory.shared.impl.IdFactory;
import com.google.gwt.requestfactory.shared.impl.MessageFactoryHolder;
import com.google.gwt.requestfactory.shared.impl.SimpleProxyId;
import com.google.gwt.requestfactory.shared.messages.IdMessage;
import com.google.gwt.requestfactory.shared.messages.IdMessage.Strength;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;
/**
* Encapsulates all state relating to the processing of a single request so that
* the SimpleRequestProcessor can be stateless.
*/
class RequestState implements EntityCodex.EntitySource {
final IdToEntityMap beans = new IdToEntityMap();
private final Map<Object, SimpleProxyId<?>> domainObjectsToId;
private final IdFactory idFactory;
private final ServiceLayer service;
private final Resolver resolver;
public RequestState(RequestState parent) {
idFactory = parent.idFactory;
domainObjectsToId = parent.domainObjectsToId;
service = parent.service;
resolver = new Resolver(this);
}
public RequestState(final ServiceLayer service) {
this.service = service;
idFactory = new IdFactory() {
@Override
public boolean isEntityType(Class<?> clazz) {
return EntityProxy.class.isAssignableFrom(clazz);
}
@Override
public boolean isValueType(Class<?> clazz) {
return ValueProxy.class.isAssignableFrom(clazz);
}
@Override
@SuppressWarnings("unchecked")
protected <P extends BaseProxy> Class<P> getTypeFromToken(String typeToken) {
return (Class<P>) service.resolveClass(typeToken);
}
@Override
protected String getTypeToken(Class<? extends BaseProxy> clazz) {
return service.resolveTypeToken(clazz);
}
};
domainObjectsToId = new IdentityHashMap<Object, SimpleProxyId<?>>();
resolver = new Resolver(this);
}
public Splittable flatten(Object domainValue) {
Splittable flatValue;
if (ValueCodex.canDecode(domainValue.getClass())) {
flatValue = ValueCodex.encode(domainValue);
} else {
flatValue = new SimpleRequestProcessor(service).createOobMessage(Collections.singletonList(domainValue));
}
return flatValue;
}
public <Q extends BaseProxy> AutoBean<Q> getBeanForPayload(IdMessage idMessage) {
SimpleProxyId<Q> id;
if (Strength.SYNTHETIC.equals(idMessage.getStrength())) {
@SuppressWarnings("unchecked")
Class<Q> clazz = (Class<Q>) service.resolveClass(idMessage.getTypeToken());
id = idFactory.allocateSyntheticId(clazz, idMessage.getSyntheticId());
} else {
String decodedId = idMessage.getServerId() == null ? null
: SimpleRequestProcessor.fromBase64(idMessage.getServerId());
id = idFactory.getId(idMessage.getTypeToken(), decodedId,
idMessage.getClientId());
}
return getBeanForPayload(id);
}
public <Q extends BaseProxy> AutoBean<Q> getBeanForPayload(
SimpleProxyId<Q> id, Object domainObject) {
@SuppressWarnings("unchecked")
AutoBean<Q> toReturn = (AutoBean<Q>) beans.get(id);
if (toReturn == null) {
toReturn = createEntityProxyBean(id, domainObject);
}
return toReturn;
}
/**
* EntityCodex support.
*/
public <Q extends BaseProxy> AutoBean<Q> getBeanForPayload(
Splittable serializedProxyId) {
IdMessage idMessage = AutoBeanCodex.decode(MessageFactoryHolder.FACTORY,
IdMessage.class, serializedProxyId).as();
return getBeanForPayload(idMessage);
}
public IdFactory getIdFactory() {
return idFactory;
}
public Resolver getResolver() {
return resolver;
}
/**
* EntityCodex support. This method is identical to
* {@link IdFactory#getHistoryToken(SimpleProxyId)} except that it
* base64-encodes the server ids.
* <p>
* XXX: Merge this with AbstsractRequestContext's implementation
*/
public Splittable getSerializedProxyId(SimpleProxyId<?> stableId) {
AutoBean<IdMessage> bean = MessageFactoryHolder.FACTORY.id();
IdMessage ref = bean.as();
ref.setTypeToken(service.resolveTypeToken(stableId.getProxyClass()));
if (stableId.isSynthetic()) {
ref.setStrength(Strength.SYNTHETIC);
ref.setSyntheticId(stableId.getSyntheticId());
} else if (stableId.isEphemeral()) {
ref.setStrength(Strength.EPHEMERAL);
ref.setClientId(stableId.getClientId());
} else {
ref.setServerId(SimpleRequestProcessor.toBase64(stableId.getServerId()));
}
return AutoBeanCodex.encode(bean);
}
public ServiceLayer getServiceLayer() {
return service;
}
/**
* If the given domain object has been previously associated with an id,
* return it
*/
public SimpleProxyId<?> getStableId(Object domain) {
return domainObjectsToId.get(domain);
}
/**
* EntityCodex support.
*/
public boolean isEntityType(Class<?> clazz) {
return idFactory.isEntityType(clazz);
}
/**
* EntityCodex support.
*/
public boolean isValueType(Class<?> clazz) {
return idFactory.isValueType(clazz);
}
/**
* Creates an AutoBean for the given id, tracking a domain object.
*/
private <Q extends BaseProxy> AutoBean<Q> createEntityProxyBean(
SimpleProxyId<Q> id, Object domainObject) {
AutoBean<Q> toReturn = AutoBeanFactoryMagic.createBean(id.getProxyClass(),
SimpleRequestProcessor.CONFIGURATION);
toReturn.setTag(Constants.STABLE_ID, id);
toReturn.setTag(Constants.DOMAIN_OBJECT, domainObject);
beans.put(id, toReturn);
return toReturn;
}
/**
* Returns the AutoBean corresponding to the given id, or creates if it does
* not yet exist.
*/
private <Q extends BaseProxy> AutoBean<Q> getBeanForPayload(
SimpleProxyId<Q> id) {
@SuppressWarnings("unchecked")
AutoBean<Q> toReturn = (AutoBean<Q>) beans.get(id);
if (toReturn == null) {
// Resolve the domain object
Class<?> domainClass = service.resolveDomainClass(id.getProxyClass());
Object domain;
if (id.isEphemeral() || id.isSynthetic()) {
domain = service.createDomainObject(domainClass);
if (domain == null) {
throw new UnexpectedException("Could not create instance of "
+ domainClass.getCanonicalName(), null);
}
} else {
Splittable address = StringQuoter.split(id.getServerId());
Class<?> param = service.getIdType(domainClass);
Object domainParam;
if (ValueCodex.canDecode(param)) {
domainParam = ValueCodex.decode(param, address);
} else {
domainParam = new SimpleRequestProcessor(service).decodeOobMessage(
param, address).get(0);
}
domain = service.loadDomainObject(domainClass, domainParam);
}
domainObjectsToId.put(domain, id);
toReturn = createEntityProxyBean(id, domain);
}
return toReturn;
}
}