blob: f5f3c1a3dda835d0a7d2bebad4b98def50090d5d [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.client.impl;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.requestfactory.client.impl.messages.JsonResults;
import com.google.gwt.requestfactory.client.impl.messages.JsonServerException;
import com.google.gwt.requestfactory.client.impl.messages.RelatedObjects;
import com.google.gwt.requestfactory.client.impl.messages.RequestData;
import com.google.gwt.requestfactory.client.impl.messages.ReturnRecord;
import com.google.gwt.requestfactory.shared.EntityProxy;
import com.google.gwt.requestfactory.shared.EntityProxyId;
import com.google.gwt.requestfactory.shared.InstanceRequest;
import com.google.gwt.requestfactory.shared.Receiver;
import com.google.gwt.requestfactory.shared.Request;
import com.google.gwt.requestfactory.shared.RequestContext;
import com.google.gwt.requestfactory.shared.ServerFailure;
import com.google.gwt.requestfactory.shared.Violation;
import com.google.gwt.requestfactory.shared.WriteOperation;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* <p>
* <span style="color:red">Experimental API: This class is still under rapid
* development, and is very likely to be deleted. Use it at your own risk.
* </span>
* </p>
* Abstract implementation of {@link Request}. Each request stores a
* {@link DeltaValueStoreJsonImpl}.
*
* @param <T> return type
*/
public abstract class AbstractRequest<T> implements Request<T>,
InstanceRequest<EntityProxy, T> {
/**
* Used by generated subtypes.
*/
protected final Set<String> propertyRefs = new HashSet<String>();
protected final AbstractRequestContext requestContext;
private Receiver<? super T> receiver;
private RequestData requestData;
protected AbstractRequest(AbstractRequestContext requestContext) {
this.requestContext = requestContext;
}
public void fire() {
requestContext.fire();
}
public void fire(Receiver<? super T> receiver) {
to(receiver);
fire();
}
/**
* @return the properties
*/
public Set<String> getPropertyRefs() {
return Collections.unmodifiableSet(propertyRefs);
}
public RequestData getRequestData() {
if (requestData == null) {
requestData = makeRequestData();
}
return requestData;
}
public void handleResponseText(String responseText) {
JsonResults results = JsonResults.fromResults(responseText);
JsonServerException cause = results.getException();
if (cause != null) {
fail(new ServerFailure(cause.getMessage(), cause.getType(),
cause.getTrace()));
return;
}
// handle violations
JsArray<ReturnRecord> violationsArray = results.getViolations();
if (violationsArray != null) {
processViolations(violationsArray);
} else {
requestContext.processSideEffects(results.getSideEffects());
processRelated(results.getRelated());
if (results.isNullResult()) {
// Indicates the server explicitly meant to send a null value
succeed(null);
} else {
handleResult(results.getResult());
}
}
}
public RequestContext to(Receiver<? super T> receiver) {
this.receiver = receiver;
return requestContext;
}
/**
* This method comes from the {@link InstanceRequest} interface. Instance
* methods place the instance in the first parameter slot.
*/
public Request<T> using(EntityProxy instanceObject) {
getRequestData().getParameters()[0] = instanceObject;
/*
* Instance methods enqueue themselves when their using() method is called.
* This ensures that the instance parameter will have been set when
* AbstractRequestContext.retainArg() is called.
*/
requestContext.addInvocation(this);
return this;
}
public Request<T> with(String... propertyRefs) {
this.propertyRefs.addAll(Arrays.asList(propertyRefs));
return this;
}
/**
* This method is called by generated subclasses to process the main return
* property of the JSON payload. The return record isn't just a reference to a
* persist or update side-effect, so it has to be processed separately.
*/
protected <Q extends EntityProxy> Q decodeReturnObject(Class<Q> clazz,
Object obj) {
ReturnRecord jso = (ReturnRecord) obj;
SimpleEntityProxyId<Q> id = requestContext.getRequestFactory().getId(clazz,
jso.getSimpleId());
Q proxy = requestContext.processReturnRecord(id, (ReturnRecord) obj,
WriteOperation.UPDATE);
return proxy;
}
protected <Q extends EntityProxy> void decodeReturnObjectList(
Class<Q> elementType, Object obj, Collection<Q> accumulator) {
@SuppressWarnings("unchecked")
JsArray<ReturnRecord> array = (JsArray<ReturnRecord>) obj;
for (int i = 0, j = array.length(); i < j; i++) {
ReturnRecord record = array.get(i);
if (record == null) {
accumulator.add(null);
continue;
}
// Decode the individual object
Q decoded = decodeReturnObject(elementType, record);
// Really want Class.isInstance()
assert elementType.equals(decoded.stableId().getProxyClass());
accumulator.add(decoded);
}
}
protected <Q> void decodeReturnValueList(Class<Q> elementType, Object obj,
Collection<Q> accumulator) {
@SuppressWarnings("unchecked")
List<Q> temp = (List<Q>) EntityCodex.decode(List.class, elementType,
requestContext, obj);
accumulator.addAll(temp);
}
protected void fail(ServerFailure failure) {
requestContext.reuse();
if (receiver != null) {
receiver.onFailure(failure);
}
}
/**
* Process the response and call {@link #succeed(Object) or
* #fail(com.google.gwt.requestfactory.shared.ServerFailure).
*/
protected abstract void handleResult(Object result);
protected boolean hasReceiver() {
return receiver != null;
}
protected abstract RequestData makeRequestData();
protected void processRelated(RelatedObjects related) {
for (String token : related.getHistoryTokens()) {
SimpleEntityProxyId<EntityProxy> id = requestContext.getRequestFactory().getProxyId(
token);
requestContext.processReturnRecord(id, related.getReturnRecord(token));
}
}
protected void succeed(T t) {
// The user may not have called to()
if (receiver != null) {
receiver.onSuccess(t);
}
}
private void processViolations(JsArray<ReturnRecord> violationsArray) {
int length = violationsArray.length();
Set<Violation> errors = new HashSet<Violation>(length);
for (int i = 0; i < length; i++) {
ReturnRecord violationRecord = violationsArray.get(i);
final EntityProxyId<?> key = requestContext.getRequestFactory().getId(
violationRecord.getSchema(), violationRecord.getEncodedId(),
violationRecord.getFutureId());
HashMap<String, String> violations = new HashMap<String, String>();
assert violationRecord.hasViolations();
violationRecord.fillViolations(violations);
for (Map.Entry<String, String> entry : violations.entrySet()) {
final String path = entry.getKey();
final String message = entry.getValue();
errors.add(new Violation() {
public String getMessage() {
return message;
}
public String getPath() {
return path;
}
public EntityProxyId<?> getProxyId() {
return key;
}
});
}
}
requestContext.reuse();
requestContext.addErrors(errors);
if (receiver != null) {
receiver.onViolation(errors);
}
}
}