/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores
* CA 94065 USA or visit www.oracle.com if you need additional information or
* have any questions.
*/
package com.sun.lwuit.io;
import com.sun.lwuit.Dialog;
import com.sun.lwuit.Display;
import com.sun.lwuit.events.ActionEvent;
import com.sun.lwuit.events.ActionListener;
import com.sun.lwuit.io.impl.IOImplementation;
import com.sun.lwuit.io.util.BufferedInputStream;
import com.sun.lwuit.io.util.BufferedOutputStream;
import com.sun.lwuit.io.util.IOProgressListener;
import com.sun.lwuit.io.util.Util;
import com.sun.lwuit.util.EventDispatcher;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
/**
* This class represents a connection object in the form of a request response
* typically common for HTTP/HTTPS connections. Elements of this type are
* placed in a priority queue based
*
* @author Shai Almog
*/
public class ConnectionRequest implements IOProgressListener {
/**
* A critical priority request will "push" through the queue to the highest point
* regardless of anything else and ignoring anything that is not in itself of
* critical priority.
* A critical priority will stop any none critical connection in progress
*/
public static final byte PRIORITY_CRITICAL = (byte)100;
/**
* A high priority request is the second highest level, it will act exactly like
* a critical priority with one difference. It doesn't block another incoming high priority
* request. E.g. if a high priority request
*/
public static final byte PRIORITY_HIGH = (byte)80;
/**
* Normal priority executes as usual on the queue
*/
public static final byte PRIORITY_NORMAL = (byte)50;
/**
* Low priority requests are mostly background tasks that should still be accomplished though
*/
public static final byte PRIORITY_LOW = (byte)30;
/**
* Redundant elements can be discarded from the queue when paused
*/
public static final byte PRIORITY_REDUNDANT = (byte)0;
private EventDispatcher actionListeners;
/**
* @return the defaultFollowRedirects
*/
public static boolean isDefaultFollowRedirects() {
return defaultFollowRedirects;
}
/**
* @param aDefaultFollowRedirects the defaultFollowRedirects to set
*/
public static void setDefaultFollowRedirects(boolean aDefaultFollowRedirects) {
defaultFollowRedirects = aDefaultFollowRedirects;
}
private byte priority = PRIORITY_NORMAL;
private long timeSinceLastUpdate;
private Hashtable requestArguments;
private boolean post = true;
private String contentType = "application/x-www-form-urlencoded; charset=UTF-8";
private static String defaultUserAgent = null;
private String userAgent = getDefaultUserAgent();
private String url;
private boolean writeRequest;
private boolean readRequest = true;
private boolean paused;
private boolean killed = false;
private static boolean defaultFollowRedirects = true;
private boolean followRedirects = defaultFollowRedirects;
private int timeout = 300000;
private InputStream input;
private OutputStream output;
private int progress = NetworkEvent.PROGRESS_TYPE_OUTPUT;
private int contentLength = -1;
private boolean duplicateSupported;
private EventDispatcher responseCodeListeners;
private Hashtable userHeaders;
private Dialog showOnInit;
private Dialog disposeOnCompletion;
private int silentRetryCount = 0;
/**
* Adds the given header to the request that will be sent
*
* @param key the header key
* @param value the header value
*/
public void addRequestHeader(String key, String value) {
if(userHeaders == null) {
userHeaders = new Hashtable();
}
userHeaders.put(key, value);
}
/**
* Adds the given header to the request that will be sent unless the header
* is already set to something else
*
* @param key the header key
* @param value the header value
*/
void addRequestHeaderDontRepleace(String key, String value) {
if(userHeaders == null) {
userHeaders = new Hashtable();
}
if(!userHeaders.containsKey(key)) {
userHeaders.put(key, value);
}
}
void prepare() {
timeSinceLastUpdate = System.currentTimeMillis();
}
/**
* Invoked to initialize HTTP headers, cookies etc.
*
* @param connection the connection object
*/
protected void initConnection(Object connection) {
timeSinceLastUpdate = System.currentTimeMillis();
IOImplementation impl = IOImplementation.getInstance();
impl.setPostRequest(connection, isPost());
if(getUserAgent() != null) {
impl.setHeader(connection, "User-Agent", getUserAgent());
}
if(getContentType() != null) {
impl.setHeader(connection, "Content-Type", getContentType());
}
if(userHeaders != null) {
Enumeration e = userHeaders.keys();
while(e.hasMoreElements()) {
String k = (String)e.nextElement();
String value = (String)userHeaders.get(k);
impl.setHeader(connection, k, value);
}
}
}
/**
* Performs the actual network request on behalf of the network manager
*/
void performOperation() throws IOException {
if(shouldStop()) {
return;
}
IOImplementation impl = IOImplementation.getInstance();
Object connection = null;
input = null;
output = null;
try {
String actualUrl = createRequestURL();
connection = impl.connect(actualUrl, isReadRequest(), isPost() || isWriteRequest());
if(shouldStop()) {
return;
}
initConnection(connection);
Vector v = impl.getCookiesForURL(actualUrl);
if(v != null) {
int c = v.size();
if(c > 0) {
StringBuffer cookieStr = new StringBuffer();
Cookie first = (Cookie)v.elementAt(0);
cookieStr.append(first.getName());
cookieStr.append("=");
cookieStr.append(first.getValue());
for(int iter = 1 ; iter < c ; iter++) {
Cookie current = (Cookie)v.elementAt(iter);
cookieStr.append(";");
cookieStr.append(current.getName());
cookieStr.append("=");
cookieStr.append(current.getValue());
}
impl.setHeader(connection, "cookie", cookieStr.toString());
}
}
if(isWriteRequest()) {
output = impl.openOutputStream(connection);
if(shouldStop()) {
return;
}
if(NetworkManager.getInstance().hasProgressListeners() && output instanceof BufferedOutputStream) {
((BufferedOutputStream)output).setProgressListener(this);
}
buildRequestBody(output);
if(shouldStop()) {
return;
}
if(output instanceof BufferedOutputStream) {
((BufferedOutputStream)output).flushBuffer();
if(shouldStop()) {
return;
}
}
}
timeSinceLastUpdate = System.currentTimeMillis();
int responseCode = impl.getResponseCode(connection);
String[] cookies = impl.getHeaderFields("Set-Cookie", connection);
if(cookies != null && cookies.length > 0){
Vector cook = new Vector();
for(int iter = 0 ; iter < cookies.length ; iter++) {
Cookie coo = parseCookieHeader(cookies[iter]);
if(coo != null) {
cook.addElement(coo);
}
}
Cookie [] arr = new Cookie[cook.size()];
for (int i = 0; i < arr.length; i++) {
arr[i] = (Cookie) cook.elementAt(i);
}
impl.addCookie(arr);
}
if(responseCode != 200) {
// redirect to new location
if(followRedirects && (responseCode == 301 || responseCode == 302
|| responseCode == 303)) {
String uri = impl.getHeaderField("location", connection);
if(!(uri.startsWith("http://") || uri.startsWith("https://"))) {
// relative URI's in the location header are illegal but some sites mistakenly use them
url = Util.relativeToAbsolute(url, uri);
} else {
url = uri;
}
if((responseCode == 302 || responseCode == 303)){
if(this.post && shouldConvertPostToGetOnRedirect()) {
this.post = false;
setWriteRequest(false);
}
}
impl.cleanup(output);
impl.cleanup(connection);
connection = null;
output = null;
if(!onRedirect(url)){
retry();
}
return;
}
handleErrorResponseCode(responseCode, impl.getResponseMessage(connection));
return;
}
readHeaders(connection);
contentLength = impl.getContentLength(connection);
timeSinceLastUpdate = System.currentTimeMillis();
progress = NetworkEvent.PROGRESS_TYPE_INPUT;
if(isReadRequest()) {
input = impl.openInputStream(connection);
if(shouldStop()) {
return;
}
if(input instanceof BufferedInputStream) {
if(NetworkManager.getInstance().hasProgressListeners()) {
((BufferedInputStream)input).setProgressListener(this);
}
((BufferedInputStream)input).setYield(getYield());
}
readResponse(input);
if(shouldAutoCloseResponse()) {
input.close();
}
input = null;
}
} finally {
// always cleanup connections/streams even in case of an exception
impl.cleanup(output);
impl.cleanup(input);
impl.cleanup(connection);
timeSinceLastUpdate = -1;
input = null;
output = null;
connection = null;
}
}
/**
* This mimics the behavior of browsers that convert post operations to get operations when redirecting a
* request.
* @return defaults to true, this case be modified by subclasses
*/
protected boolean shouldConvertPostToGetOnRedirect() {
return true;
}
/**
* Allows reading the headers from the connection by calling the getHeader() method.
* @param connection used when invoking getHeader
* @throws java.io.IOException thrown on failure
*/
protected void readHeaders(Object connection) throws IOException {
}
/**
* Returns the HTTP header field for the given connection, this method is only guaranteed to work
* when invoked from the readHeaders method.
*
* @param connection the connection to the network
* @param header the name of the header
* @return the value of the header
* @throws java.io.IOException thrown on failure
*/
protected String getHeader(Object connection, String header) throws IOException {
return IOImplementation.getInstance().getHeaderField(header, connection);
}
/**
* Returns the amount of time to yield for other processes, this is an implicit
* method that automatically generates values for lower priority connections
* @return yield duration or -1 for no yield
*/
protected int getYield() {
if(priority > PRIORITY_NORMAL) {
return -1;
}
if(priority == PRIORITY_NORMAL) {
return 20;
}
return 40;
}
/**
* Indicates whether the response stream should be closed automatically by
* the framework (defaults to true), this might cause an issue if the stream
* needs to be passed to a separate thread for reading.
*
* @return true to close the response stream automatically.
*/
protected boolean shouldAutoCloseResponse() {
return true;
}
/**
* Parses a raw cookie header and returns a cookie object to send back at the server
*
* @param h raw cookie header
* @return the cookie object
*/
private Cookie parseCookieHeader(String h) {
Cookie c = new Cookie();
int edge = h.indexOf(';');
int equals = h.indexOf('=');
if(equals < 0) {
return null;
}
c.setName(h.substring(0, equals));
if(edge < 0) {
c.setValue(h.substring(equals + 1));
c.setDomain(IOImplementation.getInstance().getURLDomain(url));
return c;
}else{
c.setValue(h.substring(equals + 1, edge));
}
int index = h.indexOf("domain=");
if (index != -1) {
String domain = h.substring(index + 7);
index = domain.indexOf(';');
if (index!=-1) {
domain = domain.substring(0, index);
}
if (url.indexOf(domain) < 0) { //if (!hc.getHost().endsWith(domain)) {
System.out.println("Warning: Cookie tried to set to another domain");
c.setDomain(IOImplementation.getInstance().getURLDomain(url));
} else {
c.setDomain(domain);
}
} else {
c.setDomain(IOImplementation.getInstance().getURLDomain(url));
}
return c;
}
/**
* Handles IOException thrown when performing a network operation
*
* @param err the exception thrown
*/
protected void handleIOException(IOException err) {
handleException(err);
}
/**
* Handles an exception thrown when performing a network operation
*
* @param err the exception thrown
*/
protected void handleRuntimeException(RuntimeException err) {
handleException(err);
}
/**
* Handles an exception thrown when performing a network operation, the default
* implementation shows a retry dialog.
*
* @param err the exception thrown
*/
protected void handleException(Exception err) {
if(killed) {
return;
}
err.printStackTrace();
if(silentRetryCount > 0) {
silentRetryCount--;
retry();
return;
}
if(Display.isInitialized() &&
Dialog.show("Exception", err.toString() + ": " + err.getMessage(), "Retry", "Cancel")) {
retry();
}
}
/**
* Handles a server response code that is not 200 and not a redirect (unless redirect handling is disabled)
*
* @param code the response code from the server
* @param message the response message from the server
*/
protected void handleErrorResponseCode(int code, String message) {
if(responseCodeListeners != null) {
NetworkEvent n = new NetworkEvent(this, code, message);
responseCodeListeners.fireActionEvent(n);
return;
}
if(Display.isInitialized() &&
Dialog.show("Error", code + ": " + message, "Retry", "Cancel")) {
retry();
}
}
/**
* Retry the current operation in case of an exception
*/
public void retry() {
NetworkManager.getInstance().addToQueue(this, true);
}
/**
* This is a callback method that been called when there is a redirect.
*
* @param url the url to be redirected
* @return true if the implementation would like to handle this by itself
*/
public boolean onRedirect(String url){
return false;
}
/**
* Callback for the server response with the input stream from the server.
* This method is invoked on the network thread
*
* @param input the input stream containing the response
* @throws IOException when a read input occurs
*/
protected void readResponse(InputStream input) throws IOException {
if(hasResponseListeners()) {
byte[] data = Util.readInputStream(input);
fireResponseListener(new NetworkEvent(this, data));
}
}
/**
* Creates the request URL mostly for a get request
*
* @return the string of a request
*/
protected String createRequestURL() {
if(!post && requestArguments != null) {
StringBuffer b = new StringBuffer(url);
Enumeration e = requestArguments.keys();
if(e.hasMoreElements()) {
b.append("?");
}
while(e.hasMoreElements()) {
String key = (String)e.nextElement();
String value = (String)requestArguments.get(key);
b.append(key);
b.append("=");
b.append(value);
if(e.hasMoreElements()) {
b.append("&");
}
}
return b.toString();
}
return url;
}
/**
* Invoked when send body is true, by default sends the request arguments based
* on "POST" conventions
*
* @param os output stream of the body
*/
protected void buildRequestBody(OutputStream os) throws IOException {
if(post && requestArguments != null) {
StringBuffer val = new StringBuffer();
Enumeration e = requestArguments.keys();
while(e.hasMoreElements()) {
String key = (String)e.nextElement();
String value = (String)requestArguments.get(key);
val.append(key);
val.append("=");
val.append(value);
if(e.hasMoreElements()) {
val.append("&");
}
}
if(IOImplementation.getInstance().shouldWriteUTFAsGetBytes()) {
os.write(val.toString().getBytes("UTF-8"));
} else {
OutputStreamWriter w = new OutputStreamWriter(os, "UTF-8");
w.write(val.toString());
}
}
}
/**
* Kills this request if possible
*/
public void kill() {
killed = true;
}
/**
* Returns true if the request is paused or killed, developers should call this
* method periodically to test whether they should quit the current IO operation immediately
*
* @return true if the request is paused or killed
*/
protected boolean shouldStop() {
return isPaused() || isKilled();
}
/**
* Return true from this method if this connection can be paused and resumed later on.
* A pausable network operation receives a "pause" invocation and is expected to stop
* network operations as soon as possible. It will later on receive a resume() call and
* optionally start downloading again.
*
* @return false by default.
*/
protected boolean isPausable() {
return false;
}
/**
* Invoked to pause this opeation, this method will only be invoked if isPausable() returns true
* (its false by default). After this method is invoked current network operations should
* be stoped as soon as possible for this class.
*
* @return This method can return false to indicate that there is no need to resume this
* method since the operation has already been completed or made redundant
*/
public boolean pause() {
paused = true;
return true;
}
/**
* Called when a previously paused operation now has the networking time to resume.
* Assuming this method returns true, the network request will be resent to the server
* and the operation can resume.
*
* @return This method can return false to indicate that there is no need to resume this
* method since the operation has already been completed or made redundant
*/
public boolean resume() {
paused = false;
return true;
}
/**
* Returns true for a post operation and false for a get operation
*
* @return the post
*/
public boolean isPost() {
return post;
}
/**
* Returns true for a post operation and false for a get operation
*
* @throws IllegalStateException if invoked after an addArgument call
*/
public void setPost(boolean post) {
if(this.post != post && requestArguments != null && requestArguments.size() > 0) {
throw new IllegalStateException("Request method (post/get) can't be modified one arguments have been assigned to the request");
}
this.post = post;
if(this.post) {
setWriteRequest(true);
}
}
/**
* Add an argument to the request response
*
* @param key the key of the argument
* @param value the value for the argument
*/
private void addArg(String key, Object value) {
if(requestArguments == null) {
requestArguments = new Hashtable();
}
if(value == null || key == null){
return;
}
requestArguments.put(key, value);
}
/**
* Add an argument to the request response
*
* @param key the key of the argument
* @param value the value for the argument
* @deprecated use the version that accepts a string instead
*/
public void addArgument(String key, byte[] value) {
key = key.intern();
if(post) {
addArg(key, Util.encodeBody(value));
} else {
addArg(key, Util.encodeUrl(value));
}
}
/**
* Removes the given argument from the request
*
* @param key the key of the argument no longer used
*/
public void removeArgument(String key) {
if(requestArguments != null) {
requestArguments.remove(key);
}
}
/**
* Add an argument to the request response without encoding it, this is useful for
* arguments which are already encoded
*
* @param key the key of the argument
* @param value the value for the argument
*/
public void addArgumentNoEncoding(String key, String value) {
addArg(key, value);
}
/**
* Add an argument to the request response
*
* @param key the key of the argument
* @param value the value for the argument
*/
public void addArgument(String key, String value) {
if(post) {
addArg(key, Util.encodeBody(value));
} else {
addArg(key, Util.encodeUrl(value));
}
}
/**
* @return the contentType
*/
public String getContentType() {
return contentType;
}
/**
* @param contentType the contentType to set
*/
public void setContentType(String contentType) {
this.contentType = contentType;
}
/**
* @return the writeRequest
*/
public boolean isWriteRequest() {
return writeRequest;
}
/**
* @param writeRequest the writeRequest to set
*/
public void setWriteRequest(boolean writeRequest) {
this.writeRequest = writeRequest;
}
/**
* @return the readRequest
*/
public boolean isReadRequest() {
return readRequest;
}
/**
* @param readRequest the readRequest to set
*/
public void setReadRequest(boolean readRequest) {
this.readRequest = readRequest;
}
/**
* @return the paused
*/
protected boolean isPaused() {
return paused;
}
/**
* @param paused the paused to set
*/
protected void setPaused(boolean paused) {
this.paused = paused;
}
/**
* @return the killed
*/
protected boolean isKilled() {
return killed;
}
/**
* @param killed the killed to set
*/
protected void setKilled(boolean killed) {
this.killed = killed;
}
/**
* The priority of this connection based on the constants in this class
*
* @return the priority
*/
public byte getPriority() {
return priority;
}
/**
* The priority of this connection based on the constants in this class
*
* @param priority the priority to set
*/
public void setPriority(byte priority) {
this.priority = priority;
}
/**
* @return the userAgent
*/
public String getUserAgent() {
return userAgent;
}
/**
* @param userAgent the userAgent to set
*/
public void setUserAgent(String userAgent) {
this.userAgent = userAgent;
}
/**
* @return the defaultUserAgent
*/
public static String getDefaultUserAgent() {
return defaultUserAgent;
}
/**
* @param aDefaultUserAgent the defaultUserAgent to set
*/
public static void setDefaultUserAgent(String aDefaultUserAgent) {
defaultUserAgent = aDefaultUserAgent;
}
/**
* @return the followRedirects
*/
public boolean isFollowRedirects() {
return followRedirects;
}
/**
* @param followRedirects the followRedirects to set
*/
public void setFollowRedirects(boolean followRedirects) {
this.followRedirects = followRedirects;
}
/**
* Indicates the timeout for this connection request
*
* @return the timeout
*/
public int getTimeout() {
return timeout;
}
/**
* Indicates the timeout for this connection request
*
* @param timeout the timeout to set
*/
public void setTimeout(int timeout) {
this.timeout = timeout;
}
/**
* This method prevents a manual timeout from occuring when invoked at a frequency faster
* than the timeout.
*/
void updateActivity() {
timeSinceLastUpdate = System.currentTimeMillis();
}
/**
* Returns the time since the last activity update
*/
int getTimeSinceLastActivity() {
if(input != null && input instanceof BufferedInputStream) {
long t = ((BufferedInputStream)input).getLastActivityTime();
if(t > timeSinceLastUpdate) {
timeSinceLastUpdate = t;
}
}
if(output != null && output instanceof BufferedOutputStream) {
long t = ((BufferedOutputStream)output).getLastActivityTime();
if(t > timeSinceLastUpdate) {
timeSinceLastUpdate = t;
}
}
return (int)(System.currentTimeMillis() - timeSinceLastUpdate);
}
/**
* Returns the content legth header value
*
* @return the content length
*/
public int getContentLength() {
return contentLength;
}
/**
* @inheritDoc
*/
public void ioStreamUpdate(Object source, int bytes) {
NetworkManager.getInstance().fireProgressEvent(this, progress, contentLength, bytes);
}
/**
* @return the url
*/
public String getUrl() {
return url;
}
/**
* @param url the url to set
*/
public void setUrl(String url) {
url = url.intern();
this.url = url;
}
/**
* Adds a listener that would be notified on the LWUIT thread of a response from the server.
* This event is specific to the connection request type and its firing will change based on
* how the connection request is read/processed
*
* @param a listener
*/
public void addResponseListener(ActionListener a) {
if(actionListeners == null) {
actionListeners = new EventDispatcher();
actionListeners.setBlocking(false);
}
actionListeners.addListener(a);
}
/**
* Removes the given listener
*
* @param a listener
*/
public void removeResponseListener(ActionListener a) {
if(actionListeners == null) {
return;
}
actionListeners.removeListener(a);
if(actionListeners.getListenerVector() == null || actionListeners.getListenerVector().size() == 0) {
actionListeners = null;
}
}
/**
* Adds a listener that would be notified on the LWUIT thread of a response code that
* is not a 200 (OK) or 301/2 (redirect) response code.
*
* @param a listener
*/
public void addResponseCodeListener(ActionListener a) {
if(responseCodeListeners == null) {
responseCodeListeners = new EventDispatcher();
responseCodeListeners.setBlocking(false);
}
responseCodeListeners.addListener(a);
}
/**
* Removes the given listener
*
* @param a listener
*/
public void removeResponseCodeListener(ActionListener a) {
if(responseCodeListeners == null) {
return;
}
responseCodeListeners.removeListener(a);
if(responseCodeListeners.getListenerVector() == null || responseCodeListeners.getListenerVector().size() == 0) {
responseCodeListeners = null;
}
}
/**
* Returns true if someone is listening to action response events, this is useful
* so we can decide whether to bother collecting data for an event in some cases
* since building the event object might be memory/CPU intensive.
*
* @return true or false
*/
protected boolean hasResponseListeners() {
return actionListeners != null;
}
/**
* Fires the response event to the listeners on this connection
*
* @param ev the event to fire
*/
protected void fireResponseListener(ActionEvent ev) {
if(actionListeners != null) {
actionListeners.fireActionEvent(ev);
}
}
/**
* Indicates whether this connection request supports duplicate entries in the request queue
*
* @return the duplicateSupported value
*/
public boolean isDuplicateSupported() {
return duplicateSupported;
}
/**
* Indicates whether this connection request supports duplicate entries in the request queue
*
* @param duplicateSupported the duplicateSupported to set
*/
public void setDuplicateSupported(boolean duplicateSupported) {
this.duplicateSupported = duplicateSupported;
}
/**
* @inheritDoc
*/
public int hashCode() {
if(url != null) {
int i = url.hashCode();
if(requestArguments != null) {
i = i ^ requestArguments.hashCode();
}
return i;
}
return 0;
}
/**
* @inheritDoc
*/
public boolean equals(Object o) {
if(o != null && o.getClass() == getClass()) {
ConnectionRequest r = (ConnectionRequest)o;
// interned string comparison
if(r.url == url) {
if(requestArguments != null) {
if(r.requestArguments != null && requestArguments.size() == r.requestArguments.size()) {
Enumeration e = requestArguments.keys();
while(e.hasMoreElements()) {
Object key = e.nextElement();
Object value = requestArguments.get(key);
Object otherValue = r.requestArguments.get(key);
if(otherValue == null || !value.equals(otherValue)) {
return false;
}
}
return true;
}
} else {
if(r.requestArguments == null) {
return true;
}
}
}
}
return false;
}
void validateImpl() {
if(url == null) {
throw new IllegalStateException("URL is null");
}
if(url.length() == 0) {
throw new IllegalStateException("URL is empty");
}
validate();
}
/**
* Validates that the request has the required information before being added to the queue
* e.g. checks if the URL is null. This method should throw an IllegalStateException for
* a case where one of the values required for this connection request is missing.
* This method can be overriden by subclasses to add additional tests. It is usefull
* to do tests here since the exception will be thrown immediately when invoking addToQueue
* which is more intuitive to debug than the alternative.
*/
protected void validate() {
if(!url.startsWith("http")) {
throw new IllegalStateException("Only HTTP urls are supported!");
}
}
/**
* A dialog that will be seamlessly disposed once the given request has been completed
*
* @return the disposeOnCompletion
*/
public Dialog getDisposeOnCompletion() {
return disposeOnCompletion;
}
/**
* A dialog that will be seamlessly disposed once the given request has been completed
*
* @param disposeOnCompletion the disposeOnCompletion to set
*/
public void setDisposeOnCompletion(Dialog disposeOnCompletion) {
this.disposeOnCompletion = disposeOnCompletion;
}
/**
* This dialog will be shown when this request enters the network queue
*
* @return the showOnInit
*/
public Dialog getShowOnInit() {
return showOnInit;
}
/**
* This dialog will be shown when this request enters the network queue
*
* @param showOnInit the showOnInit to set
*/
public void setShowOnInit(Dialog showOnInit) {
this.showOnInit = showOnInit;
}
/**
* Indicates the number of times to silentry retyr a connection that failed
* before prompting
*
* @return the silentRetryCount
*/
public int getSilentRetryCount() {
return silentRetryCount;
}
/**
* Indicates the number of times to silentry retyr a connection that failed
* before prompting
* @param silentRetryCount the silentRetryCount to set
*/
public void setSilentRetryCount(int silentRetryCount) {
this.silentRetryCount = silentRetryCount;
}
}