/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.eas.client;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.eas.application.Loader;
import com.eas.client.metadata.Fields;
import com.eas.client.metadata.Parameter;
import com.eas.client.metadata.Parameters;
import com.eas.client.published.PublishedFile;
import com.eas.client.queries.Query;
import com.eas.client.serial.QueryJSONReader;
import com.eas.client.xhr.FormData;
import com.eas.client.xhr.ProgressEvent;
import com.eas.client.xhr.ProgressHandler;
import com.eas.client.xhr.ProgressHandlerAdapter;
import com.eas.client.xhr.XMLHttpRequest2;
import com.eas.core.Cancellable;
import com.eas.core.Utils;
import com.google.gwt.core.client.Callback;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.AnchorElement;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.event.dom.client.LoadEvent;
import com.google.gwt.event.dom.client.LoadHandler;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.regexp.shared.MatchResult;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.safehtml.shared.SafeUri;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Frame;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.xhr.client.ReadyStateChangeHandler;
import com.google.gwt.xhr.client.XMLHttpRequest;
import com.google.gwt.xhr.client.XMLHttpRequest.ResponseType;
import com.google.gwt.xml.client.Document;
import com.google.gwt.xml.client.XMLParser;
/**
*
* @author mg
*/
public class AppClient {
//
private static final RegExp httpPrefixPattern = RegExp.compile("https?://.*");
public static final String APPLICATION_URI = "/application";
private static String sourcePath = "/";
private static boolean simpleModules;
public static final String REPORT_LOCATION_CONTENT_TYPE = "text/platypus-report-location";
//
private static AppClient appClient;
private boolean cacheBustEnabled;
private String apiUrl;
private String principal;
private Map<String, Document> documents = new HashMap<String, Document>();
private Map<String, ModuleStructure> modulesStructures = new HashMap<String, ModuleStructure>();
private Map<String, Query> queries = new HashMap<String, Query>();
private Map<String, Object> serverModules = new HashMap<>();
public static class ModuleStructure {
protected Set<String> structure = new HashSet<>();
protected Set<String> clientDependencies = new HashSet<>();
protected Set<String> serverDependencies = new HashSet<>();
protected Set<String> queriesDependencies = new HashSet<>();
public ModuleStructure(String aRelativeFileName) {
super();
structure.add(aRelativeFileName);
}
public ModuleStructure(Set<String> aStructure, Set<String> aClientDependencies, Set<String> aServerDependencies, Set<String> aQueriesDependencies) {
super();
structure.addAll(aStructure);
clientDependencies.addAll(aClientDependencies);
serverDependencies.addAll(aServerDependencies);
queriesDependencies.addAll(aQueriesDependencies);
}
public ModuleStructure(String aRelativeFileName, JsArrayString prefetchedResources, JsArrayString autoDependencies, JsArrayString rpcDependencies, JsArrayString entitiesDependencies) {
this(aRelativeFileName);
if (prefetchedResources != null) {
for (int i = 0; i < prefetchedResources.length(); i++) {
structure.add(prefetchedResources.get(i));
}
}
if (autoDependencies != null) {
for (int i = 0; i < autoDependencies.length(); i++) {
clientDependencies.add(autoDependencies.get(i));
}
}
if (rpcDependencies != null) {
for (int i = 0; i < rpcDependencies.length(); i++) {
serverDependencies.add(rpcDependencies.get(i));
}
}
if (entitiesDependencies != null) {
for (int i = 0; i < entitiesDependencies.length(); i++) {
queriesDependencies.add(entitiesDependencies.get(i));
}
}
}
public Set<String> getStructure() {
return Collections.unmodifiableSet(structure);
}
public Set<String> getClientDependencies() {
return Collections.unmodifiableSet(clientDependencies);
}
public Set<String> getQueriesDependencies() {
return Collections.unmodifiableSet(queriesDependencies);
}
public Set<String> getServerDependencies() {
return Collections.unmodifiableSet(serverDependencies);
}
}
public static String remoteApiUri() {
NodeList<com.google.gwt.dom.client.Element> metas = com.google.gwt.dom.client.Document.get().getHead().getElementsByTagName("meta");
for (int i = 0; i < metas.getLength(); i++) {
com.google.gwt.dom.client.Element meta = metas.getItem(i);
if ("platypus-server".equalsIgnoreCase(meta.getAttribute("name"))) {
return meta.getAttribute("content");
}
}
return relativeUri();
}
public static String relativeUri() {
String pageUrl = GWT.getHostPageBaseURL();
pageUrl = pageUrl.substring(0, pageUrl.length() - 1);
return pageUrl;
}
public static AppClient getInstance() {
if (appClient == null) {
appClient = new AppClient(remoteApiUri() + APPLICATION_URI);
}
return appClient;
}
/**
* Only for tests! Don't call this method from application code!
*
* @param aClient
*/
public static void setInstance(AppClient aClient) {
appClient = aClient;
}
public static boolean isSimpleModules() {
return simpleModules;
}
public static void setSimpleModules(boolean aValue) {
simpleModules = aValue;
}
public static String getSourcePath() {
return sourcePath;
}
public static void setSourcePath(String aValue) {
if (aValue != null && !aValue.isEmpty()) {
sourcePath = aValue;
if (!sourcePath.endsWith("/")) {
sourcePath = sourcePath + "/";
}
if (!sourcePath.startsWith("/")) {
sourcePath = "/" + sourcePath;
}
} else {
sourcePath = "/";
}
}
public SafeUri getResourceUri(final String aResourceName) {
return new SafeUri() {
@Override
public String asString() {
MatchResult htppMatch = httpPrefixPattern.exec(aResourceName);
if (htppMatch != null) {
return aResourceName;
} else {
return relativeUri() + getSourcePath() + aResourceName;
}
}
};
}
public static String toFilyAppModuleId(String aRelative, String aStartPoint) {
Element moduleIdNormalizer = com.google.gwt.dom.client.Document.get().createDivElement();
moduleIdNormalizer.setInnerHTML("<a href=\"" + aStartPoint + "/" + aRelative + "\">o</a>");
String mormalizedAbsoluteModuleUrl = URL.decode(moduleIdNormalizer.getFirstChildElement().<AnchorElement> cast().getHref());
String hostContextPrefix = AppClient.relativeUri() + AppClient.getSourcePath();
Element hostContextNormalizer = com.google.gwt.dom.client.Document.get().createDivElement();
hostContextNormalizer.setInnerHTML("<a href=\"" + hostContextPrefix + "\">o</a>");
String mormalizedHostContextPrefix = URL.decode(hostContextNormalizer.getFirstChildElement().<AnchorElement> cast().getHref());
return mormalizedAbsoluteModuleUrl.substring(mormalizedHostContextPrefix.length());
}
public static Object jsLoad(String aResourceName, final JavaScriptObject onSuccess, final JavaScriptObject onFailure) throws Exception {
return _jsLoad(aResourceName, true, onSuccess, onFailure);
}
public static Object jsLoadText(String aResourceName, final JavaScriptObject onSuccess, final JavaScriptObject onFailure) throws Exception {
return _jsLoad(aResourceName, false, onSuccess, onFailure);
}
public static Object _jsLoad(final String aResourceName, boolean aBinary, final JavaScriptObject onSuccess, final JavaScriptObject onFailure) throws Exception {
final String callerDir = Utils.lookupCallerJsDir();
SafeUri uri = AppClient.getInstance().getResourceUri(aResourceName.startsWith("./") || aResourceName.startsWith("../") ? toFilyAppModuleId(aResourceName, callerDir) : aResourceName);
if (onSuccess != null) {
AppClient.getInstance().startRequest(uri, aBinary ? ResponseType.ArrayBuffer : ResponseType.Default, new CallbackAdapter<XMLHttpRequest, XMLHttpRequest>() {
@Override
protected void doWork(XMLHttpRequest aResult) throws Exception {
if (aResult.getStatus() == Response.SC_OK) {
String responseType = aResult.getResponseType();
if (ResponseType.ArrayBuffer.getResponseTypeString().equalsIgnoreCase(responseType)) {
Utils.JsObject buffer = (Utils.JsObject) Utils.toJs(aResult.getResponseArrayBuffer());
int length = buffer.getInteger("byteLength");
buffer.setInteger("length", length);
Utils.executeScriptEventVoid(onSuccess, onSuccess, buffer);
} else {
Utils.executeScriptEventVoid(onSuccess, onSuccess, Utils.toJs(aResult.getResponseText()));
}
} else {
if (onFailure != null) {
Utils.executeScriptEventVoid(onFailure, onFailure, Utils.toJs(aResult.getStatusText()));
}
}
}
@Override
public void onFailure(XMLHttpRequest aResult) {
if (onFailure != null) {
try {
Utils.executeScriptEventVoid(onFailure, onFailure,
Utils.toJs(aResult.getStatus() != 0 ? aResult.getStatusText() : "Request has been cancelled. See browser's console for more details."));
} catch (Exception ex) {
Logger.getLogger(AppClient.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
});
} else {
XMLHttpRequest2 executed = AppClient.getInstance().syncRequest(uri.asString(), ResponseType.Default);
if (executed != null) {
if (executed.getStatus() == Response.SC_OK) {
String responseType = executed.getResponseType();
if (ResponseType.ArrayBuffer.getResponseTypeString().equalsIgnoreCase(responseType)) {
Utils.JsObject buffer = (Utils.JsObject) executed.getResponseArrayBuffer();
int length = buffer.getInteger("byteLength");
buffer.setInteger("length", length);
return buffer;
} else {
return Utils.toJs(executed.getResponseText());
}
} else {
throw new Exception(executed.getStatusText());
}
}
}
return null;
}
public static JavaScriptObject jsUpload(PublishedFile aFile, String aName, final JavaScriptObject aCompleteCallback, final JavaScriptObject aProgresssCallback,
final JavaScriptObject aErrorCallback) {
if (aFile != null) {
Cancellable cancellable = AppClient.getInstance().startUploadRequest(aFile, aName, new Callback<ProgressEvent, String>() {
protected boolean completed;
public void onSuccess(ProgressEvent aResult) {
try {
if (!completed) {
if (aProgresssCallback != null) {
Utils.executeScriptEventVoid(aProgresssCallback, aProgresssCallback, aResult);
}
if (aResult.isComplete() && aResult.getRequest() != null) {
completed = true;
if (aCompleteCallback != null) {
Utils.executeScriptEventVoid(aCompleteCallback, aCompleteCallback, Utils.JsObject.parseJSON(aResult.getRequest().getResponseText()));
}
}
}
} catch (Exception ex) {
Logger.getLogger(AppClient.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void onFailure(String reason) {
if (aErrorCallback != null) {
try {
Utils.executeScriptEventVoid(aErrorCallback, aErrorCallback, Utils.toJs(reason));
} catch (Exception ex) {
Logger.getLogger(AppClient.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
});
return Utils.publishCancellable(cancellable);
} else
return null;
}
public Cancellable startUploadRequest(final PublishedFile aFile, String aName, final Callback<ProgressEvent, String> aCallback) {
final XMLHttpRequest2 req = XMLHttpRequest.create().<XMLHttpRequest2> cast();
req.open("post", apiUrl);
final ProgressHandler handler = new ProgressHandlerAdapter() {
@Override
public void onProgress(ProgressEvent aEvent) {
try {
if (aCallback != null)
aCallback.onSuccess(aEvent);
} catch (Exception ex) {
Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void onLoadEnd(XMLHttpRequest xhr) {
try {
if (aCallback != null)
aCallback.onSuccess(ProgressEvent.create(aFile.getSize(), aFile.getSize(), null));
} catch (Exception ex) {
Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, null, ex);
}
};
@Override
public void onTimeOut(XMLHttpRequest xhr) {
if (aCallback != null) {
try {
aCallback.onFailure("timeout");
} catch (Exception ex) {
Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
@Override
public void onAbort(XMLHttpRequest xhr) {
if (aCallback != null) {
try {
aCallback.onFailure("aborted");
} catch (Exception ex) {
Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
@Override
public void onError(XMLHttpRequest xhr) {
if (aCallback != null) {
try {
aCallback.onFailure(xhr.getStatusText());
} catch (Exception ex) {
Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
};
if (req.getUpload() != null) {
req.getUpload().setOnProgress(handler);
req.getUpload().setOnLoadEnd(handler);
req.getUpload().setOnAbort(handler);
req.getUpload().setOnError(handler);
req.getUpload().setOnTimeOut(handler);
}
FormData fd = FormData.create();
fd.append(aFile.getName(), aFile, aName);
req.overrideMimeType("multipart/form-data");
// Must set the onreadystatechange handler before calling send().
req.setOnReadyStateChange(new ReadyStateChangeHandler() {
@Override
public void onReadyStateChange(XMLHttpRequest xhr) {
if (xhr.getReadyState() == XMLHttpRequest.DONE) {
xhr.clearOnReadyStateChange();
if (xhr.getStatus() == Response.SC_OK) {
try {
if (aCallback != null)
aCallback.onSuccess(ProgressEvent.create(aFile.getSize(), aFile.getSize(), xhr));
} catch (Exception ex) {
Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
if (xhr.getStatus() == 0)
handler.onAbort(xhr);
else
handler.onError(xhr);
}
}
}
});
req.send(fd);
return new Cancellable() {
@Override
public void cancel() {
req.abort();
}
};
}
public Cancellable submitForm(String aAction, RequestBuilder.Method aMethod, String aContentType, Map<String, String> aFormData, final Callback<XMLHttpRequest, XMLHttpRequest> aCallback) {
final XMLHttpRequest req = XMLHttpRequest.create().cast();
String urlPath = aAction != null ? aAction : "";
List<String> parameters = new ArrayList<String>();
for (String paramName : aFormData.keySet()) {
parameters.add(param(paramName, aFormData.get(paramName)));
}
String paramsData = params(parameters.toArray(new String[] {}));
if(aMethod != RequestBuilder.POST){
urlPath += "?" + paramsData;
}
req.open(aMethod.toString(), urlPath);
req.setRequestHeader("Content-Type", aContentType);
req.setOnReadyStateChange(new ReadyStateChangeHandler() {
@Override
public void onReadyStateChange(final XMLHttpRequest xhr) {
if (xhr.getReadyState() == XMLHttpRequest.DONE) {
xhr.clearOnReadyStateChange();
if (aCallback != null) {
try {
if (xhr.getStatus() == Response.SC_OK) {
aCallback.onSuccess(xhr);
} else {
aCallback.onFailure(xhr);
}
} catch (Exception ex) {
Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
});
if(aMethod == RequestBuilder.POST){
req.send(paramsData);
} else{
req.send();
}
return new Cancellable() {
@Override
public void cancel() {
req.abort();
}
};
}
public static String param(String aName, String aValue) {
return aName + "=" + (aValue != null ? URL.encodePathSegment(aValue) : "");
}
public static String params(String... aParams) {
String res = "";
for (int i = 0; i < aParams.length; i++) {
if (aParams[i] != null && !aParams[i].isEmpty()) {
if (!res.isEmpty()) {
res += "&";
}
res += aParams[i];
}
}
return res;
}
public AppClient(String aApiUrl) {
super();
apiUrl = aApiUrl;
}
public Document getModelDocument(String aModuleName) {
ModuleStructure structure = modulesStructures.get(aModuleName);
for (String part : structure.getStructure()) {
if (part.toLowerCase().endsWith(".model")) {
return documents.get(part);
}
}
return null;
}
public Document getFormDocument(String aModuleName) {
ModuleStructure structure = modulesStructures.get(aModuleName);
for (String part : structure.getStructure()) {
if (part.toLowerCase().endsWith(".layout")) {
return documents.get(part);
}
}
return null;
}
private String params(Parameters parameters) {
String[] res = new String[parameters.getParametersCount()];
for (int i = 0; i < parameters.getParametersCount(); i++) {
Parameter p = parameters.get(i + 1);// parameters and fields are
// 1-based
String sv = Utils.jsonStringify(p.getJsValue());
res[i] = param(p.getName(), sv);
}
return params(res);
}
public JavaScriptObject jsLogout(final JavaScriptObject onSuccess, final JavaScriptObject onFailure) throws Exception {
return Utils.publishCancellable(requestLogout(new CallbackAdapter<XMLHttpRequest, XMLHttpRequest>() {
@Override
protected void doWork(XMLHttpRequest aResult) throws Exception {
Utils.invokeJsFunction(onSuccess);
}
@Override
public void onFailure(XMLHttpRequest reason) {
try {
Utils.executeScriptEventVoid(onFailure, onFailure, Utils.toJs(reason.getStatusText()));
} catch (Exception ex) {
Logger.getLogger(AppClient.class.getName()).log(Level.SEVERE, null, ex);
}
}
}));
}
public Cancellable requestLogout(final Callback<XMLHttpRequest, XMLHttpRequest> aCallback) throws Exception {
String query = param(PlatypusHttpRequestParams.TYPE, String.valueOf(Requests.rqLogout));
return startApiRequest(null, query, null, RequestBuilder.GET, null, new CallbackAdapter<XMLHttpRequest, XMLHttpRequest>() {
@Override
protected void doWork(XMLHttpRequest aResult) throws Exception {
principal = null;
aCallback.onSuccess(aResult);
}
@Override
public void onFailure(XMLHttpRequest reason) {
aCallback.onFailure(reason);
}
});
}
public Cancellable startApiRequest(String aUrlPrefix, final String aUrlQuery, String aBody, RequestBuilder.Method aMethod, String aContentType, Callback<XMLHttpRequest, XMLHttpRequest> aCallback)
throws Exception {
String url = apiUrl + (aUrlPrefix != null ? aUrlPrefix : "") + (aUrlQuery != null ? "?" + aUrlQuery : "");
final XMLHttpRequest req = XMLHttpRequest.create();
req.open(aMethod.toString(), url);
if (aContentType != null && !aContentType.isEmpty()) {
req.setRequestHeader("Content-Type", aContentType);
}
interceptRequest(req);
req.setRequestHeader("Pragma", "no-cache");
return startRequest(req, aBody, aCallback);
}
public Cancellable startRequest(SafeUri aUri, ResponseType aResponseType, Callback<XMLHttpRequest, XMLHttpRequest> aCallback) throws Exception {
final XMLHttpRequest req = XMLHttpRequest.create();
req.open(RequestBuilder.GET.toString(), aUri.asString());
interceptRequest(req);
if (aResponseType != null && aResponseType != ResponseType.Default)
req.setResponseType(aResponseType);
req.setRequestHeader("Pragma", "no-cache");
return startRequest(req, null, aCallback);
}
public Cancellable startRequest(SafeUri aUri, Callback<XMLHttpRequest, XMLHttpRequest> aCallback) throws Exception {
return startRequest(aUri, ResponseType.Default, aCallback);
}
public Cancellable startRequest(final XMLHttpRequest req, String aBody, final Callback<XMLHttpRequest, XMLHttpRequest> aCallback) throws Exception {
// Must set the onreadystatechange handler before calling send().
req.setOnReadyStateChange(new ReadyStateChangeHandler() {
public void onReadyStateChange(XMLHttpRequest xhr) {
if (xhr.getReadyState() == XMLHttpRequest.DONE) {
xhr.clearOnReadyStateChange();
/*
* We cannot use cancel here because it would clear the
* contents of the JavaScript XmlHttpRequest object so we
* manually null out our reference to the JavaScriptObject
*/
String errorMsg = XMLHttpRequest2.getBrowserSpecificFailure(xhr);
if (errorMsg != null) {
Logger.getLogger(AppClient.class.getName()).log(Level.SEVERE, errorMsg);
try {
if (aCallback != null)
aCallback.onFailure(xhr);
} catch (Exception ex) {
Logger.getLogger(AppClient.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
try {
if (xhr.getStatus() == Response.SC_OK) {
if (aCallback != null)
aCallback.onSuccess(xhr);
} else {
if (aCallback != null)
aCallback.onFailure(xhr);
}
} catch (Exception ex) {
Logger.getLogger(AppClient.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
});
if (aBody != null && !aBody.isEmpty())
req.send(aBody);
else
req.send();
return new Cancellable() {
@Override
public void cancel() {
req.clearOnReadyStateChange();
req.abort();
}
};
}
public void startDownloadRequest(String aUrlPrefix, final int aRequestType, Map<String, String> aParams, RequestBuilder.Method aMethod) throws Exception {
final Frame frame = new Frame();
frame.setVisible(false);
frame.addLoadHandler(new LoadHandler() {
@Override
public void onLoad(LoadEvent event) {
Timer timer = new Timer() {
@Override
public void run() {
frame.removeFromParent();
}
};
timer.schedule(2000);
}
});
String query = "";
for (Map.Entry<String, String> ent : aParams.entrySet()) {
query += param(ent.getKey(), ent.getValue()) + "&";
}
query += param(PlatypusHttpRequestParams.TYPE, String.valueOf(aRequestType));
frame.setUrl(apiUrl + aUrlPrefix + "?" + query);
RootPanel.get().add(frame);
}
public XMLHttpRequest2 syncRequest(String aUrl, ResponseType aResponseType) throws Exception {
final XMLHttpRequest2 req = syncRequest(aUrl, aResponseType, null, RequestBuilder.GET);
if (req.getStatus() == Response.SC_OK)
return req;
else
throw new Exception(req.getStatus() + " " + req.getStatusText());
}
public XMLHttpRequest2 syncApiRequest(String aUrlPrefix, final String aUrlQuery, ResponseType aResponseType) throws Exception {
String url = apiUrl + (aUrlPrefix != null ? aUrlPrefix : "") + "?" + aUrlQuery;
final XMLHttpRequest2 req = syncRequest(url, aResponseType, null, RequestBuilder.GET);
if (req.getStatus() == Response.SC_OK)
return req;
else
throw new Exception(req.getStatus() + " " + req.getStatusText());
}
public XMLHttpRequest2 syncRequest(String aUrl, ResponseType aResponseType, String aBody, RequestBuilder.Method aMethod) throws Exception {
final XMLHttpRequest2 req = XMLHttpRequest.create().<XMLHttpRequest2> cast();
aUrl = Loader.URL_QUERY_PROCESSOR.process(aUrl);
req.open(aMethod.toString(), aUrl, false);
interceptRequest(req);
/*
* Since W3C standard about sync XmlHttpRequest and response type. if
* (aResponseType != null && aResponseType != ResponseType.Default)
* req.setResponseType(aResponseType);
*/
req.setRequestHeader("Pragma", "no-cache");
if (aBody != null)
req.send(aBody);
else
req.send();
if (req.getStatus() == Response.SC_OK)
return req;
else
throw new Exception(req.getStatus() + " " + req.getStatusText());
}
protected void interceptRequest(XMLHttpRequest req) {
// No-op here. Some implementation is in the tests.
}
public Cancellable requestCommit(final JavaScriptObject changeLog, final Callback<Void, String> aCallback) throws Exception {
String query = param(PlatypusHttpRequestParams.TYPE, String.valueOf(Requests.rqCommit));
return startApiRequest(null, query, Utils.JsObject.writeJSON(changeLog), RequestBuilder.POST, "application/json; charset=utf-8", new CallbackAdapter<XMLHttpRequest, XMLHttpRequest>() {
@Override
public void doWork(XMLHttpRequest aResponse) throws Exception {
Logger.getLogger(AppClient.class.getName()).log(Level.INFO, "Commit succeded: " + aResponse.getStatus() + " " + aResponse.getStatusText());
if (aCallback != null)
aCallback.onSuccess(null);
}
@Override
public void onFailure(XMLHttpRequest aResponse) {
Logger.getLogger(AppClient.class.getName()).log(Level.INFO, "Commit failed: " + aResponse.getStatus() + " " + aResponse.getStatusText());
if (aCallback != null)
aCallback.onFailure(aResponse.getStatusText());
}
});
}
public void jsLoggedInUser(final JavaScriptObject onSuccess, final JavaScriptObject onFailure) throws Exception {
requestLoggedInUser(new CallbackAdapter<String, String>() {
@Override
protected void doWork(String aResult) throws Exception {
if (onSuccess != null) {
onSuccess.<Utils.JsObject> cast().call(null, Utils.toJs(aResult));
}
}
@Override
public void onFailure(String reason) {
if (onFailure != null) {
onFailure.<Utils.JsObject> cast().call(null, Utils.toJs(reason));
}
}
});
}
public void requestLoggedInUser(final Callback<String, String> aCallback) throws Exception {
if (principal == null) {
String query = param(PlatypusHttpRequestParams.TYPE, String.valueOf(Requests.rqCredential));
startApiRequest(null, query, "", RequestBuilder.GET, null, new CallbackAdapter<XMLHttpRequest, XMLHttpRequest>() {
@Override
protected void doWork(XMLHttpRequest aResponse) throws Exception {
if (isJsonResponse(aResponse)) {
String respText = aResponse.getResponseText();
Object oResult = respText != null && !respText.isEmpty() ? Utils.toJava(Utils.jsonParse(respText)) : null;
assert oResult == null || oResult instanceof JavaScriptObject : "Credential request expects null or JavaScriptObject value as a response.";
JavaScriptObject jsObject = (JavaScriptObject) oResult;
Object oUserName = jsObject.<Utils.JsObject> cast().getJava("userName");
assert oUserName == null || oUserName instanceof String : "Credential request expects null or String value as a user name.";
principal = (String) oUserName;
if (principal == null || principal.isEmpty())
principal = "anonymous" + String.valueOf(IdGenerator.genId());
if (aCallback != null) {
aCallback.onSuccess(principal);
}
} else {
if (aCallback != null) {
aCallback.onFailure(aResponse.getResponseText());
}
}
}
@Override
public void onFailure(XMLHttpRequest reason) {
principal = "anonymous" + String.valueOf(IdGenerator.genId());
if (aCallback != null)
aCallback.onFailure(reason.getStatus() + " : " + reason.getStatusText());
}
});
} else {
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
if (aCallback != null) {
aCallback.onSuccess(principal);
}
}
});
}
}
public ModuleStructure getModuleStructure(String aModuleName) {
return modulesStructures.get(aModuleName);
}
public ModuleStructure putModuleStructure(String aModuleName, ModuleStructure aStructure) {
return modulesStructures.put(aModuleName, aStructure);
}
private native static void checkModulesIndex(AppClient aClient)/*-{
if ($wnd.define) {
var index = $wnd.define['modules-index'];
for ( var fileName in index) {
var structure = index[fileName];
var mstructure = @com.eas.client.AppClient.ModuleStructure::new(Ljava/lang/String;Lcom/google/gwt/core/client/JsArrayString;Lcom/google/gwt/core/client/JsArrayString;Lcom/google/gwt/core/client/JsArrayString;Lcom/google/gwt/core/client/JsArrayString;)(fileName, structure.prefetched, structure['global-deps'], structure.rpc, structure.entities);
var defaultModuleName = fileName;
if (defaultModuleName.endsWith('.js')) {
defaultModuleName = defaultModuleName.substring(0, defaultModuleName.length - 3);
}
aClient.@com.eas.client.AppClient::putModuleStructure(Ljava/lang/String;Lcom/eas/client/AppClient$ModuleStructure;)(defaultModuleName, mstructure);
if (structure.modules) {
for ( var i = 0; i < structure.modules.length; i++) {
aClient.@com.eas.client.AppClient::putModuleStructure(Ljava/lang/String;Lcom/eas/client/AppClient$ModuleStructure;)(structure.modules[i], mstructure);
}
}
}
$wnd.define['modules-index'] = {};
}
}-*/;
public Cancellable requestModuleStructure(final String aModuleName, final Callback<ModuleStructure, XMLHttpRequest> aCallback) throws Exception {
checkModulesIndex(this);
if (modulesStructures.containsKey(aModuleName)) {
if (aCallback != null) {
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
aCallback.onSuccess(modulesStructures.get(aModuleName));
}
});
}
return new Cancellable() {
@Override
public void cancel() {
}
};
} else if (simpleModules) {
if (aCallback != null) {
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
String fakeRelativeFileName = aModuleName;
if (!fakeRelativeFileName.toLowerCase().endsWith(".js")) {
fakeRelativeFileName = fakeRelativeFileName + ".js";
}
ModuleStructure s = new ModuleStructure(fakeRelativeFileName);
aCallback.onSuccess(s);
}
});
}
return new Cancellable() {
@Override
public void cancel() {
}
};
} else {
String query = params(param(PlatypusHttpRequestParams.TYPE, String.valueOf(Requests.rqModuleStructure)), param(PlatypusHttpRequestParams.MODULE_NAME, aModuleName));
query = Loader.URL_QUERY_PROCESSOR.process(query);
return startApiRequest(null, query, "", RequestBuilder.GET, null, new CallbackAdapter<XMLHttpRequest, XMLHttpRequest>() {
@Override
public void doWork(XMLHttpRequest aResponse) throws Exception {
if (isJsonResponse(aResponse)) {
// Some post processing
String text = aResponse.getResponseText();
JavaScriptObject _doc = text != null && !text.isEmpty() ? Utils.JsObject.parseJSON(text) : null;
Utils.JsObject doc = _doc.cast();
//
Set<String> structure = new HashSet<String>();
Set<String> clientDependencies = new HashSet<String>();
Set<String> queryDependencies = new HashSet<String>();
Set<String> serverModuleDependencies = new HashSet<String>();
Utils.JsObject jsStructure = doc.getJs("structure").cast();
Utils.JsObject jsClientDependencies = doc.getJs("clientDependencies").cast();
Utils.JsObject jsQueryDependencies = doc.getJs("queryDependencies").cast();
Utils.JsObject jsServerDependencies = doc.getJs("serverDependencies").cast();
for (int i = 0; i < jsStructure.length(); i++) {
structure.add(jsStructure.getStringSlot(i));
}
for (int i = 0; i < jsClientDependencies.length(); i++) {
clientDependencies.add(jsClientDependencies.getStringSlot(i));
}
for (int i = 0; i < jsQueryDependencies.length(); i++) {
queryDependencies.add(jsQueryDependencies.getStringSlot(i));
}
for (int i = 0; i < jsServerDependencies.length(); i++) {
serverModuleDependencies.add(jsServerDependencies.getStringSlot(i));
}
ModuleStructure moduleStructure = new ModuleStructure(structure, clientDependencies, serverModuleDependencies, queryDependencies);
modulesStructures.put(aModuleName, moduleStructure);
if (aCallback != null) {
aCallback.onSuccess(moduleStructure);
}
} else {
if (aCallback != null)
aCallback.onFailure(aResponse);
}
}
@Override
public void onFailure(XMLHttpRequest reason) {
if (aCallback != null)
aCallback.onFailure(reason);
}
});
}
}
public Cancellable requestDocument(final String aResourceName, final Callback<Document, XMLHttpRequest> aCallback) throws Exception {
if (documents.containsKey(aResourceName)) {
final Document doc = documents.get(aResourceName);
// doc may be null, because of application elements without a
// xml-dom, plain scripts for example.
if (aCallback != null) {
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
aCallback.onSuccess(doc);
}
});
}
return new Cancellable() {
@Override
public void cancel() {
// no op here because of no request have been sent
}
};
} else {
SafeUri documentUri = new SafeUri() {
@Override
public String asString() {
return checkedCacheBust(relativeUri() + getSourcePath() + aResourceName);
}
};
return startRequest(documentUri, new CallbackAdapter<XMLHttpRequest, XMLHttpRequest>() {
@Override
public void doWork(XMLHttpRequest aResponse) throws Exception {
// Some post processing
String text = aResponse.getResponseText();
Document doc = text != null && !text.isEmpty() ? XMLParser.parse(text) : null;
documents.put(aResourceName, doc);
//
if (aCallback != null)
aCallback.onSuccess(doc);
}
@Override
public void onFailure(XMLHttpRequest reason) {
if (aCallback != null)
aCallback.onFailure(reason);
}
});
}
}
public Cancellable requestServerModule(final String aModuleName, final Callback<Void, String> aCallback) throws Exception {
if (isServerModule(aModuleName)) {
if (aCallback != null) {
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
aCallback.onSuccess(null);
}
});
}
return new Cancellable() {
@Override
public void cancel() {
// no op here because of no request have been sent
}
};
} else {
String query = params(param(PlatypusHttpRequestParams.TYPE, String.valueOf(Requests.rqCreateServerModule)), param(PlatypusHttpRequestParams.MODULE_NAME, aModuleName));
return startApiRequest(null, query, "", RequestBuilder.GET, null, new CallbackAdapter<XMLHttpRequest, XMLHttpRequest>() {
@Override
public void doWork(XMLHttpRequest aResponse) throws Exception {
// Some post processing
if (isJsonResponse(aResponse)) {
addServerModule(aModuleName, aResponse.getResponseText());
if (aCallback != null) {
aCallback.onSuccess(null);
}
} else {
onFailure(aResponse);
}
}
@Override
public void onFailure(XMLHttpRequest aResponse) {
if (aCallback != null) {
String responseText = aResponse.getResponseText();
aCallback.onFailure(responseText != null && !responseText.isEmpty() ? responseText : aResponse.getStatusText());
}
}
});
}
}
public Object getServerModule(String aModuleName) {
return serverModules.get(aModuleName);
}
public void addServerModule(String aModuleName, String aStructure) throws Exception {
serverModules.put(aModuleName, Utils.jsonParse(aStructure));
}
public boolean isServerModule(String aModuleName) throws Exception {
return serverModules.containsKey(aModuleName);
}
public static native JavaScriptObject createReport(JavaScriptObject Report, String reportLocation)/*-{
return new Report(reportLocation);
}-*/;
public boolean isCacheBustEnabled() {
return cacheBustEnabled;
}
public void setCacheBustEnabled(boolean aValue) {
cacheBustEnabled = aValue;
}
public Object requestServerMethodExecution(final String aModuleName, final String aMethodName, final JsArrayString aParams, final JavaScriptObject onSuccess, final JavaScriptObject onFailure,
final JavaScriptObject aReportConstructor) throws Exception {
String[] convertedParams = new String[aParams.length()];
for (int i = 0; i < aParams.length(); i++)
convertedParams[i] = param(PlatypusHttpRequestParams.PARAMS_ARRAY, aParams.get(i));
String query = params(param(PlatypusHttpRequestParams.TYPE, String.valueOf(Requests.rqExecuteServerModuleMethod)), param(PlatypusHttpRequestParams.MODULE_NAME, aModuleName),
param(PlatypusHttpRequestParams.METHOD_NAME, aMethodName), params(convertedParams));
if (onSuccess != null) {
startApiRequest(null, null, query, RequestBuilder.POST, "application/x-www-form-urlencoded; charset=utf-8", new CallbackAdapter<XMLHttpRequest, XMLHttpRequest>() {
@Override
public void doWork(XMLHttpRequest aResponse) throws Exception {
if (isJsonResponse(aResponse)) {
Utils.executeScriptEventVoid(onSuccess, onSuccess, Utils.toJs(aResponse.getResponseText()));
// WARNING!!!Don't edit to Utils.jsonParse!
// It is parsed in high-level js-code.
} else if (isReportResponse(aResponse)) {
Utils.executeScriptEventVoid(onSuccess, onSuccess, createReport(aReportConstructor, aResponse.getResponseText()));
} else {
Utils.executeScriptEventVoid(onSuccess, onSuccess, Utils.toJs(aResponse.getResponseText()));
}
}
@Override
public void onFailure(XMLHttpRequest aResponse) {
if (onFailure != null) {
try {
String responseText = aResponse.getResponseText();
if (isJsonResponse(aResponse)) {
Utils.executeScriptEventVoid(onFailure, onFailure, Utils.jsonParse(responseText));
} else {
Utils.executeScriptEventVoid(onFailure, onFailure, Utils.toJs(responseText != null && !responseText.isEmpty() ? responseText : aResponse.getStatusText()));
}
} catch (Exception ex) {
Logger.getLogger(AppClient.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
});
return null;
} else {
XMLHttpRequest2 executed = syncApiRequest(null, query, ResponseType.Default);
if (executed != null) {
if (executed.getStatus() == Response.SC_OK) {
String responseType = executed.getResponseHeader("content-type");
if (responseType != null) {
if (isJsonResponse(executed)) {
// WARNING!!!Don't edit to Utils.jsonParse!
// It is parsed in high-level js-code.
return Utils.toJs(executed.getResponseText());
} else if (responseType.toLowerCase().contains(REPORT_LOCATION_CONTENT_TYPE)) {
return createReport(aReportConstructor, executed.getResponseText());
} else {
return Utils.toJs(executed.getResponseText());
}
} else {
return Utils.toJs(executed.getResponseText());
}
} else {
String responseText = executed.getResponseText();
throw new Exception(responseText != null && !responseText.isEmpty() ? responseText : executed.getStatusText());
}
} else {
return null;
}
}
}
public Cancellable requestAppQuery(final String queryName, final Callback<Query, String> aCallback) throws Exception {
final Query alreadyQuery = queries.get(queryName);
if (alreadyQuery != null) {
if (aCallback != null) {
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
aCallback.onSuccess(alreadyQuery);
}
});
}
return new Cancellable() {
@Override
public void cancel() {
// no op here because of no request have been sent
}
};
} else {
String urlQuery = params(param(PlatypusHttpRequestParams.TYPE, String.valueOf(Requests.rqAppQuery)), param(PlatypusHttpRequestParams.QUERY_ID, queryName));
return startApiRequest(null, urlQuery, "", RequestBuilder.GET, null, new CallbackAdapter<XMLHttpRequest, XMLHttpRequest>() {
@Override
public void doWork(XMLHttpRequest aResponse) throws Exception {
if (isJsonResponse(aResponse)) {
// Some post processing
Query query = readQuery(aResponse);
query.setClient(AppClient.this);
//
queries.put(queryName, query);
if (aCallback != null)
aCallback.onSuccess(query);
} else {
if (aCallback != null) {
aCallback.onFailure(aResponse.getResponseText());
}
}
}
private Query readQuery(XMLHttpRequest aResponse) throws Exception {
return QueryJSONReader.read(Utils.JsObject.parseJSON(aResponse.getResponseText()));
}
@Override
public void onFailure(XMLHttpRequest aResponse) {
if (aCallback != null) {
aCallback.onFailure(aResponse.getStatusText());
}
}
});
}
}
public Query getCachedAppQuery(String aQueryId) {
Query query = queries.get(aQueryId);
if (query != null) {
AppClient client = query.getClient();
query = query.copy();
query.setClient(client);
}
return query;
}
public Cancellable requestData(String aQueryName, Parameters aParams, final Fields aExpectedFields, final Callback<JavaScriptObject, String> aCallback) throws Exception {
String query = params(param(PlatypusHttpRequestParams.TYPE, String.valueOf(Requests.rqExecuteQuery)), param(PlatypusHttpRequestParams.QUERY_ID, aQueryName), params(aParams));
return startApiRequest(null, query, "", RequestBuilder.GET, null, new CallbackAdapter<XMLHttpRequest, XMLHttpRequest>() {
@Override
public void doWork(XMLHttpRequest aResponse) throws Exception {
if (isJsonResponse(aResponse)) {
JavaScriptObject parsed = Utils.JsObject.parseJSONDateReviver(aResponse.getResponseText());
if (aCallback != null)
aCallback.onSuccess(parsed);
} else {
if (aCallback != null)
aCallback.onFailure(aResponse.getResponseText());
}
}
@Override
public void onFailure(XMLHttpRequest aResponse) {
if (aCallback != null) {
int status = aResponse.getStatus();
String statusText = aResponse.getStatusText();
if (statusText == null || statusText.isEmpty())
statusText = null;
if (status == 0)
Logger.getLogger(AppClient.class.getName()).log(Level.WARNING, "Data recieving is aborted");
aCallback.onFailure(statusText);
}
}
});
}
public String checkedCacheBust(String aUrl) {
return cacheBustEnabled ? aUrl + "?" + PlatypusHttpRequestParams.CACHE_BUSTER + "=" + IdGenerator.genId() : aUrl;
}
private boolean isJsonResponse(XMLHttpRequest aResponse) {
String responseType = aResponse.getResponseHeader("content-type");
if (responseType != null) {
responseType = responseType.toLowerCase();
return responseType.contains("application/json") || responseType.contains("application/javascript") || responseType.contains("text/json") || responseType.contains("text/javascript");
} else {
return false;
}
}
private boolean isReportResponse(XMLHttpRequest aResponse) {
String responseType = aResponse.getResponseHeader("content-type");
if (responseType != null) {
responseType = responseType.toLowerCase();
return responseType.contains(REPORT_LOCATION_CONTENT_TYPE);
} else {
return false;
}
}
}