/*
* Copyright (c) 2011-2014 Fernando Petrola
*
* 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.dragome.web.services;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Proxy;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Map.Entry;
import org.w3c.dom.EventHandler;
import org.w3c.dom.XMLHttpRequest;
import org.w3c.dom.events.Event;
import com.dragome.commons.DelegateCode;
import com.dragome.commons.ExecutionHandler;
import com.dragome.commons.javascript.ScriptHelper;
import com.dragome.services.AsyncCallbackWrapper;
import com.dragome.services.ServiceLocator;
import com.dragome.services.WebServiceLocator;
import com.dragome.services.interfaces.AsyncCallback;
import com.dragome.services.interfaces.AsyncResponseHandler;
import com.dragome.services.interfaces.RequestExecutor;
import com.dragome.web.debugging.messages.ClientToServerServiceInvocationHandler;
public class RequestExecutorImpl implements RequestExecutor
{
public interface XMLHttpRequestExtension extends XMLHttpRequest
{
@DelegateCode(eval= "this.node.send()")
public String sendSync();
@DelegateCode(eval= "this.node.send($1)")
public String sendSync(Object data);
}
private boolean useGetMethod;
public RequestExecutorImpl(boolean useGetMethod)
{
this.useGetMethod= useGetMethod;
}
public String executeSynchronousRequest(String url, Map<String, String> parameters)
{
final ExecutionHandler executionHandler= ServiceLocator.getInstance().getConfigurator().getExecutionHandler();
if (executionHandler.canSuspend())
{
final String[] resultArray= new String[] { "" };
AsyncCallback<String> asyncCallbackToContinue= new AsyncCallback<String>()
{
public void onSuccess(String result)
{
resultArray[0]= result;
executionHandler.continueExecution();
}
public void onError()
{
throw new RuntimeException("Error in async call");
}
};
AsyncCallback<String> wrappedCallback= wrapCallback(Serializable.class, asyncCallbackToContinue);
executeHttpRequest(true, url, parameters, wrappedCallback, false, useGetMethod);
executionHandler.suspendExecution();
return resultArray[0];
}
else
return executeHttpRequest(false, url, parameters, null, false, useGetMethod);
}
public String executeFixedSynchronousRequest(String url, Map<String, String> parameters)
{
return executeHttpRequest(false, url, parameters, null, false, useGetMethod);
}
public static String executeHttpRequest(boolean isAsync, String url, Map<String, String> parameters, final AsyncCallback<String> asyncCallback, boolean crossDomain, boolean useGetMethod)
{
try
{
return executeHttpRequestInternal(isAsync, url, parameters, asyncCallback, useGetMethod);
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
private static String executeHttpRequestInternal(boolean isAsync, String url, Map<String, String> parameters, final AsyncCallback<String> asyncCallback, boolean useGetMethod) throws UnsupportedEncodingException
{
ScriptHelper.put("url", url, null);
final XMLHttpRequestExtension xmlHttpRequest= ScriptHelper.evalCasting("new XMLHttpRequest()", XMLHttpRequestExtension.class, null);
xmlHttpRequest.open(useGetMethod ? "GET" : "POST", url, isAsync);
xmlHttpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
String parameter= "";
if (parameters != null)
for (Entry<String, String> entry : parameters.entrySet())
parameter+= URLEncoder.encode(entry.getKey(), "UTF-8") + "=" + URLEncoder.encode(entry.getValue(), "UTF-8") + "&";
if (isAsync)
{
xmlHttpRequest.setOnreadystatechange(new EventHandler()
{
public void handleEvent(Event evt)
{
if (xmlHttpRequest.getReadyState() == 4 && xmlHttpRequest.getStatus() == 200)
asyncCallback.onSuccess(xmlHttpRequest.getResponseText());
else if (xmlHttpRequest.getStatus() == 404)
asyncCallback.onError();
}
});
}
xmlHttpRequest.sendSync(parameter);
return isAsync ? "" : (String) xmlHttpRequest.getResponseText();
}
public String executeAsynchronousRequest(String url, Map<String, String> parameters, AsyncCallback<String> asyncCallback)
{
AsyncCallback<String> wrappedCallback= wrapCallback(Serializable.class, asyncCallback);
return executeHttpRequest(true, url, parameters, wrappedCallback, false, useGetMethod);
}
public static <T, S> AsyncCallback<S> wrapCallback(final Class<T> type, final AsyncCallback<S> asyncCallback)
{
AsyncCallback<S> result;
if (WebServiceLocator.getInstance().isRemoteDebugging())
{
result= new AsyncCallbackWrapper<S>(asyncCallback)
{
public void onError()
{
asyncCallback.onError();
}
public void onSuccess(S result)
{
AsyncResponseHandler asyncResponseHandler= createRemoteServiceByWebSocket(AsyncResponseHandler.class);
String id= (String) ScriptHelper.eval("this.javaId", this);
asyncResponseHandler.pushResponse((String) result, id);
}
};
}
else
result= (AsyncCallback<S>) asyncCallback;
return result;
}
public static <T> T createRemoteServiceByWebSocket(Class<T> type)
{
return (T) Proxy.newProxyInstance(RequestExecutorImpl.class.getClassLoader(), new Class[] { type }, new ClientToServerServiceInvocationHandler(type));
}
public void executeCrossDomainAsynchronousRequest(String url, Map<String, String> parameters, AsyncCallback<String> asyncCallback)
{
AsyncCallback<String> wrappedCallback= wrapCallback(Serializable.class, asyncCallback);
executeHttpRequest(true, url, parameters, wrappedCallback, true, useGetMethod);
}
}