All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.web.bindery.requestfactory.server.RequestState Maven / Gradle / Ivy

There is a newer version: 2.10.0
Show newest version
/*
 * 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.web.bindery.requestfactory.server;

import com.google.web.bindery.autobean.shared.AutoBean;
import com.google.web.bindery.autobean.shared.AutoBeanCodex;
import com.google.web.bindery.autobean.shared.Splittable;
import com.google.web.bindery.autobean.shared.ValueCodex;
import com.google.web.bindery.autobean.shared.impl.StringQuoter;
import com.google.web.bindery.autobean.vm.AutoBeanFactorySource;
import com.google.web.bindery.requestfactory.server.SimpleRequestProcessor.IdToEntityMap;
import com.google.web.bindery.requestfactory.shared.BaseProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxy;
import com.google.web.bindery.requestfactory.shared.ValueProxy;
import com.google.web.bindery.requestfactory.shared.impl.Constants;
import com.google.web.bindery.requestfactory.shared.impl.EntityCodex;
import com.google.web.bindery.requestfactory.shared.impl.IdFactory;
import com.google.web.bindery.requestfactory.shared.impl.MessageFactoryHolder;
import com.google.web.bindery.requestfactory.shared.impl.SimpleProxyId;
import com.google.web.bindery.requestfactory.shared.messages.IdMessage;
import com.google.web.bindery.requestfactory.shared.messages.IdMessage.Strength;

import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
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> 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 

Class

getTypeFromToken(String typeToken) { return (Class

) service.resolveClass(typeToken); } @Override protected String getTypeToken(Class clazz) { return service.resolveTypeToken(clazz); } }; domainObjectsToId = new IdentityHashMap>(); resolver = new Resolver(this); } /** * Turn a domain value into a wire format message. */ 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; } /** * Get or create a BaseProxy AutoBean for the given id. */ public AutoBean getBeanForPayload( SimpleProxyId id, Object domainObject) { @SuppressWarnings("unchecked") AutoBean toReturn = (AutoBean) beans.get(id); if (toReturn == null) { toReturn = createProxyBean(id, domainObject); } return toReturn; } /** * EntityCodex support. */ public AutoBean getBeanForPayload( Splittable serializedProxyId) { IdMessage idMessage = AutoBeanCodex.decode(MessageFactoryHolder.FACTORY, IdMessage.class, serializedProxyId).as(); @SuppressWarnings("unchecked") AutoBean toReturn = (AutoBean) getBeansForPayload( Collections.singletonList(idMessage)).get(0); return toReturn; } /** * Get or create BaseProxy AutoBeans for a list of id-bearing messages. */ public List> getBeansForPayload( List idMessages) { List> ids = new ArrayList>( idMessages.size()); for (IdMessage idMessage : idMessages) { SimpleProxyId id; if (Strength.SYNTHETIC.equals(idMessage.getStrength())) { Class clazz = 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()); } ids.add(id); } return getBeansForIds(ids); } 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. *

* XXX: Merge this with AbstsractRequestContext's implementation */ public Splittable getSerializedProxyId(SimpleProxyId stableId) { AutoBean 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 AutoBean createProxyBean( SimpleProxyId id, Object domainObject) { AutoBean toReturn = AutoBeanFactorySource.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 AutoBeans corresponding to the given ids, or creates them if * they do not yet exist. */ private List> getBeansForIds( List> ids) { List> domainClasses = new ArrayList>(ids.size()); List domainIds = new ArrayList(ids.size()); List> idsToLoad = new ArrayList>(); /* * Create proxies for ephemeral or synthetic ids that we haven't seen. Queue * up the domain ids for entities that need to be loaded. */ for (SimpleProxyId id : ids) { Class domainClass = service.resolveDomainClass(id.getProxyClass()); if (beans.containsKey(id)) { // Already have a proxy for this id, no-op } else if (id.isEphemeral() || id.isSynthetic()) { // Create a new domain object for the short-lived id Object domain = service.createDomainObject(domainClass); if (domain == null) { throw new UnexpectedException("Could not create instance of " + domainClass.getCanonicalName(), null); } AutoBean bean = createProxyBean(id, domain); beans.put(id, bean); domainObjectsToId.put(domain, id); } else { // Decode the domain parameter Splittable split = StringQuoter.split(id.getServerId()); Class param = service.getIdType(domainClass); Object domainParam; if (ValueCodex.canDecode(param)) { domainParam = ValueCodex.decode(param, split); } else { domainParam = new SimpleRequestProcessor(service).decodeOobMessage( param, split).get(0); } // Enqueue domainClasses.add(service.resolveDomainClass(id.getProxyClass())); domainIds.add(domainParam); idsToLoad.add(id); } } // Actually load the data if (!domainClasses.isEmpty()) { assert domainClasses.size() == domainIds.size() && domainClasses.size() == idsToLoad.size(); List loaded = service.loadDomainObjects(domainClasses, domainIds); if (idsToLoad.size() != loaded.size()) { throw new UnexpectedException("Expected " + idsToLoad.size() + " objects to be loaded, got " + loaded.size(), null); } Iterator itLoaded = loaded.iterator(); for (SimpleProxyId id : idsToLoad) { Object domain = itLoaded.next(); domainObjectsToId.put(domain, id); AutoBean bean = createProxyBean(id, domain); beans.put(id, bean); } } // Construct the return value List> toReturn = new ArrayList>( ids.size()); for (SimpleProxyId id : ids) { toReturn.add(beans.get(id)); } return toReturn; } }