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

com.google.web.bindery.requestfactory.shared.impl.ProxySerializerImpl 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.shared.impl;

import com.google.web.bindery.autobean.shared.AutoBean;
import com.google.web.bindery.autobean.shared.AutoBeanCodex;
import com.google.web.bindery.autobean.shared.AutoBeanFactory;
import com.google.web.bindery.autobean.shared.AutoBeanUtils;
import com.google.web.bindery.autobean.shared.AutoBeanVisitor;
import com.google.web.bindery.autobean.shared.Splittable;
import com.google.web.bindery.requestfactory.shared.BaseProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxyId;
import com.google.web.bindery.requestfactory.shared.ProxySerializer;
import com.google.web.bindery.requestfactory.shared.ProxyStore;
import com.google.web.bindery.requestfactory.shared.messages.IdMessage;
import com.google.web.bindery.requestfactory.shared.messages.OperationMessage;
import com.google.web.bindery.requestfactory.shared.messages.IdMessage.Strength;

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.
 */
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 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 bean) {
    AutoBean op = makeOperationMessage(
        serializedId(BaseProxyCategory.stableId(bean)), bean, false);

    store.put(getRequestFactory().getHistoryToken(idForSerialization),
        AutoBeanCodex.encode(op));
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy