	public void issue(Database db, ServerCredentials cred) throws APIException {
		URI uri;
		if (!query.contains("key=") && key != null) {
			String suffix = (query.contains("?")) ? "&key="+key : "?key="+key;
			query = query + suffix;
		}
		method = method.toLowerCase();
		Log.i(TAG, "Request "+ method +": " + query);
		try {
			uri = new URI(query);
		} catch (URISyntaxException e1) {
			throw new APIException(APIException.INVALID_URI, "Invalid URI: "+query, this);
		}
		HttpClient client = new DefaultHttpClient();
		client.getParams().setParameter("http.protocol.expect-continue", false);
		client.getParams().setParameter("http.protocol.content-charset", "UTF-8");
		HttpGet get = new HttpGet(uri);
		HttpPost post = new HttpPost(uri);
		HttpPut put = new HttpPut(uri);
		HttpDelete delete = new HttpDelete(uri);
		if ("post".equals(method) || "put".equals(method)) {
			if(ifMatch != null) {
				post.setHeader("If-Match", ifMatch);
				put.setHeader("If-Match", ifMatch);
			}
			if(contentType != null) {
				post.setHeader("Content-Type", contentType);
				put.setHeader("Content-Type", contentType);
			}
			if (body != null) {
				Log.d(TAG, "Request body: "+body);
				StringEntity entity;
				try {
					entity = new StringEntity(body,"UTF-8");
				} catch (UnsupportedEncodingException e) {
					throw new APIException(APIException.INVALID_UUID,
							"UnsupportedEncodingException. This shouldn't " +
							"be possible-- UTF-8 is certainly supported", this);
				}
				post.setEntity(entity);
				put.setEntity(entity);
			}
		}
		if ("get".equals(method)) {
			if(contentType != null) {
				get.setHeader("Content-Type", contentType);
			}
		}
		if ("xml".equals(disposition)) {
			XMLResponseParser parse = new XMLResponseParser(this);
			if (type == ITEM_NEW
					|| type == ITEM_ATTACHMENT_NEW) {
				parse.update(updateType, updateKey);
			}
			try {
				HttpResponse hr;
				if ("post".equals(method)) {
					hr = client.execute(post);
				} else if ("put".equals(method)) {
					hr = client.execute(put);
				} else {
					if (!"get".equals(method)) {
						throw new APIException(APIException.INVALID_METHOD,
								"Unexpected method: "+method, this);
					}
					hr = client.execute(get);
				}
				status = hr.getStatusLine().getStatusCode();
				Log.d(TAG, status + " : "+ hr.getStatusLine().getReasonPhrase());
				if (status < 400) {
					HttpEntity he = hr.getEntity();
					InputStream in = he.getContent();
					parse.setInputStream(in);
					int mode = ("put".equals(method) || type == APIRequest.ITEM_BY_KEY) ?
							XMLResponseParser.MODE_ENTRY : XMLResponseParser.MODE_FEED;
					try {
						parse.parse(mode, uri.toString(), db);
					} catch (RuntimeException e) {
						throw new RuntimeException("Parser threw exception on request: "+method+" "+query, e);
					}
				} else {
					ByteArrayOutputStream ostream = new ByteArrayOutputStream();
					hr.getEntity().writeTo(ostream);
					Log.e(TAG,"Error Body: "+ ostream.toString());
					Log.e(TAG,"Request Body:"+ body);
					if (status == 412) {
						getHandler().onError(this, APIRequest.HTTP_ERROR_CONFLICT);
					} else {
						Log.e(TAG, "Response status "+status+" : "+ostream.toString());
						getHandler().onError(this, APIRequest.HTTP_ERROR_UNSPECIFIED);
					}
					status = getHttpStatus() + REQ_FAILING;
					recordAttempt(db);
					throw new APIException(APIException.HTTP_ERROR, ostream.toString(), this);
				}
			} catch (IOException e) {
				StringBuilder sb = new StringBuilder();
				for (StackTraceElement el : e.getStackTrace()) {
					sb.append(el.toString()+"\n");
				}
				recordAttempt(db);
				throw new APIException(APIException.HTTP_ERROR,
						"An IOException was thrown: " + sb.toString(), this);
			}
		} 
		else {
			BasicResponseHandler brh = new BasicResponseHandler();
			String resp;
			try {
				if ("post".equals(method)) {
					resp = client.execute(post, brh);
				} else if ("put".equals(method)) {
					resp = client.execute(put, brh);
				} else if ("delete".equals(method)) {
					resp = client.execute(delete, brh);
				} else {
					if (!"get".equals(method)) {
						throw new APIException(APIException.INVALID_METHOD,
								"Unexpected method: "+method, this);
					}
					resp = client.execute(get, brh);
				}
			} catch (IOException e) {
				StringBuilder sb = new StringBuilder();
				for (StackTraceElement el : e.getStackTrace()) {
					sb.append(el.toString()+"\n");
				}
				recordAttempt(db);
				throw new APIException(APIException.HTTP_ERROR,
						"An IOException was thrown: " + sb.toString(), this);
			}
			if ("raw".equals(disposition)) {
				String[] keys = resp.split("\n");
				ArrayList<String> missing = new ArrayList<String>();
				if (type == ITEMS_ALL ||
						type == ITEMS_FOR_COLLECTION) {
            		int colloc = query.indexOf("/collections/");
            		int itemloc = query.indexOf("/items");
					ItemCollection coll = ItemCollection.load(
							query.substring(colloc+13, itemloc), db);
					if (coll != null) {
						coll.loadChildren(db);
						ArrayList<String> keyAL = new ArrayList<String>(Arrays.asList(keys));
						ArrayList<Item> notThere = coll.notInKeys(keyAL);
						for (Item i : notThere) {
							coll.remove(i, true, db);
						}
					}
					ArrayList<Item> recd = new ArrayList<Item>();
					for (int j = 0; j < keys.length; j++) {
						Item got = Item.load(keys[j], db);
						if (got == null) {
							missing.add(keys[j]);
						} else {
							if (coll != null) coll.add(got, true, db);
							recd.add(got);
						}
					}
					if (coll != null) {
						coll.saveChildren(db);
						coll.save(db);
					}
					Log.d(TAG, "Received "+keys.length+" keys, "+missing.size() + " missing ones");
					Log.d(TAG, "Have "+(double) recd.size() / keys.length + " of list");
					if (recd.size() == keys.length) {
						Log.d(TAG, "No new items");
						succeeded(db);
					} else if ((double) recd.size() / keys.length < REREQUEST_CUTOFF) {
						Log.d(TAG, "Requesting full list");
						APIRequest mReq;
						if (type == ITEMS_FOR_COLLECTION) {
							mReq = fetchItems(coll, false, cred);
						} else {
							mReq = fetchItems(false, cred);
						}
						mReq.status = REQ_NEW;
						mReq.save(db);
					} else {
						Log.d(TAG, "Requesting "+missing.size()+" items one by one");
						APIRequest mReq;
						for (String key : missing) {
							mReq = fetchItem(key, cred);
							mReq.status = REQ_NEW;
							mReq.save(db);
						}
						if (type == ITEMS_FOR_COLLECTION) {
							fetchItems(coll, true, cred).save(db);
						}
					}
				} else if (type == ITEMS_CHILDREN) {
            		int itemloc = query.indexOf("/items/");
            		int childloc = query.indexOf("/children");
					Item item = Item.load(
							query.substring(itemloc+7, childloc), db);
					ArrayList<Attachment> recd = new ArrayList<Attachment>();
					for (int j = 0; j < keys.length; j++) {
						Attachment got = Attachment.load(keys[j], db);
						if (got == null) missing.add(keys[j]);
						else recd.add(got);
					}
					if ((double) recd.size() / keys.length < REREQUEST_CUTOFF) {
						APIRequest mReq;
						mReq = cred.prep(children(item));
						mReq.status = REQ_NEW;
						mReq.save(db);
					} else {
						APIRequest mReq;
						for (String key : missing) {
							mReq = fetchItem(key, cred);
							mReq.status = REQ_NEW;
							mReq.save(db);
						}
					}
				}
			} else if ("json".equals(disposition)) {
			} else {
			}
			getHandler().onComplete(this);
		}
	}

	public void issue(Database db, ServerCredentials cred) throws APIException {
		URI uri;
		if (!query.contains("key=") && key != null) {
			String suffix = (query.contains("?")) ? "&key="+key : "?key="+key;
			query = query + suffix;
		}
		method = method.toLowerCase();
		Log.i(TAG, "Request "+ method +": " + query);
		try {
			uri = new URI(query);
		} catch (URISyntaxException e1) {
			throw new APIException(APIException.INVALID_URI, "Invalid URI: "+query, this);
		}
		HttpClient client = new DefaultHttpClient();
		client.getParams().setParameter("http.protocol.expect-continue", false);
		client.getParams().setParameter("http.protocol.content-charset", "UTF-8");
		HttpGet get = new HttpGet(uri);
		HttpPost post = new HttpPost(uri);
		HttpPut put = new HttpPut(uri);
		HttpDelete delete = new HttpDelete(uri);
		if ("post".equals(method) || "put".equals(method)) {
			if(ifMatch != null) {
				post.setHeader("If-Match", ifMatch);
				put.setHeader("If-Match", ifMatch);
			}
			if(contentType != null) {
				post.setHeader("Content-Type", contentType);
				put.setHeader("Content-Type", contentType);
			}
			if (body != null) {
				Log.d(TAG, "Request body: "+body);
				StringEntity entity;
				try {
					entity = new StringEntity(body,"UTF-8");
				} catch (UnsupportedEncodingException e) {
					throw new APIException(APIException.INVALID_UUID,
							"UnsupportedEncodingException. This shouldn't " +
							"be possible-- UTF-8 is certainly supported", this);
				}
				post.setEntity(entity);
				put.setEntity(entity);
			}
		}
		if ("get".equals(method)) {
			if(contentType != null) {
				get.setHeader("Content-Type", contentType);
			}
		}
		if ("xml".equals(disposition)) {
			XMLResponseParser parse = new XMLResponseParser(this);
			if (type == ITEM_NEW
					|| type == ITEM_ATTACHMENT_NEW) {
				parse.update(updateType, updateKey);
			}
			try {
				HttpResponse hr;
				if ("post".equals(method)) {
					hr = client.execute(post);
				} else if ("put".equals(method)) {
					hr = client.execute(put);
				} else {
					if (!"get".equals(method)) {
						throw new APIException(APIException.INVALID_METHOD,
								"Unexpected method: "+method, this);
					}
					hr = client.execute(get);
				}
				status = hr.getStatusLine().getStatusCode();
				Log.d(TAG, status + " : "+ hr.getStatusLine().getReasonPhrase());
				if (status < 400) {
					HttpEntity he = hr.getEntity();
					InputStream in = he.getContent();
					parse.setInputStream(in);
					int mode = ("put".equals(method) || type == APIRequest.ITEM_BY_KEY) ?
							XMLResponseParser.MODE_ENTRY : XMLResponseParser.MODE_FEED;
					try {
						parse.parse(mode, uri.toString(), db);
					} catch (RuntimeException e) {
						throw new RuntimeException("Parser threw exception on request: "+method+" "+query, e);
					}
				} else {
					ByteArrayOutputStream ostream = new ByteArrayOutputStream();
					hr.getEntity().writeTo(ostream);
					Log.e(TAG,"Error Body: "+ ostream.toString());
					Log.e(TAG,"Request Body:"+ body);
					if (status == 412) {
						getHandler().onError(this, APIRequest.HTTP_ERROR_CONFLICT);
					} else {
						Log.e(TAG, "Response status "+status+" : "+ostream.toString());
						getHandler().onError(this, APIRequest.HTTP_ERROR_UNSPECIFIED);
					}
					status = getHttpStatus() + REQ_FAILING;
					recordAttempt(db);
					throw new APIException(APIException.HTTP_ERROR, ostream.toString(), this);
				}
			} catch (Exception e) {
				StringBuilder sb = new StringBuilder();
				for (StackTraceElement el : e.getStackTrace()) {
					sb.append(el.toString()+"\n");
				}
				recordAttempt(db);
				throw new APIException(APIException.HTTP_ERROR,
						"An IOException was thrown: " + sb.toString(), this);
			}
		} 
		else {
			BasicResponseHandler brh = new BasicResponseHandler();
			String resp;
			try {
				if ("post".equals(method)) {
					resp = client.execute(post, brh);
				} else if ("put".equals(method)) {
					resp = client.execute(put, brh);
				} else if ("delete".equals(method)) {
					resp = client.execute(delete, brh);
				} else {
					if (!"get".equals(method)) {
						throw new APIException(APIException.INVALID_METHOD,
								"Unexpected method: "+method, this);
					}
					resp = client.execute(get, brh);
				}
			} catch (IOException e) {
				StringBuilder sb = new StringBuilder();
				for (StackTraceElement el : e.getStackTrace()) {
					sb.append(el.toString()+"\n");
				}
				recordAttempt(db);
				throw new APIException(APIException.HTTP_ERROR,
						"An IOException was thrown: " + sb.toString(), this);
			}
			if ("raw".equals(disposition)) {
				String[] keys = resp.split("\n");
				ArrayList<String> missing = new ArrayList<String>();
				if (type == ITEMS_ALL ||
						type == ITEMS_FOR_COLLECTION) {
            		int colloc = query.indexOf("/collections/");
            		int itemloc = query.indexOf("/items");
					ItemCollection coll = ItemCollection.load(
							query.substring(colloc+13, itemloc), db);
					if (coll != null) {
						coll.loadChildren(db);
						ArrayList<String> keyAL = new ArrayList<String>(Arrays.asList(keys));
						ArrayList<Item> notThere = coll.notInKeys(keyAL);
						for (Item i : notThere) {
							coll.remove(i, true, db);
						}
					}
					ArrayList<Item> recd = new ArrayList<Item>();
					for (int j = 0; j < keys.length; j++) {
						Item got = Item.load(keys[j], db);
						if (got == null) {
							missing.add(keys[j]);
						} else {
							if (coll != null) coll.add(got, true, db);
							recd.add(got);
						}
					}
					if (coll != null) {
						coll.saveChildren(db);
						coll.save(db);
					}
					Log.d(TAG, "Received "+keys.length+" keys, "+missing.size() + " missing ones");
					Log.d(TAG, "Have "+(double) recd.size() / keys.length + " of list");
					if (recd.size() == keys.length) {
						Log.d(TAG, "No new items");
						succeeded(db);
					} else if ((double) recd.size() / keys.length < REREQUEST_CUTOFF) {
						Log.d(TAG, "Requesting full list");
						APIRequest mReq;
						if (type == ITEMS_FOR_COLLECTION) {
							mReq = fetchItems(coll, false, cred);
						} else {
							mReq = fetchItems(false, cred);
						}
						mReq.status = REQ_NEW;
						mReq.save(db);
					} else {
						Log.d(TAG, "Requesting "+missing.size()+" items one by one");
						APIRequest mReq;
						for (String key : missing) {
							mReq = fetchItem(key, cred);
							mReq.status = REQ_NEW;
							mReq.save(db);
						}
						if (type == ITEMS_FOR_COLLECTION) {
							fetchItems(coll, true, cred).save(db);
						}
					}
				} else if (type == ITEMS_CHILDREN) {
            		int itemloc = query.indexOf("/items/");
            		int childloc = query.indexOf("/children");
					Item item = Item.load(
							query.substring(itemloc+7, childloc), db);
					ArrayList<Attachment> recd = new ArrayList<Attachment>();
					for (int j = 0; j < keys.length; j++) {
						Attachment got = Attachment.load(keys[j], db);
						if (got == null) missing.add(keys[j]);
						else recd.add(got);
					}
					if ((double) recd.size() / keys.length < REREQUEST_CUTOFF) {
						APIRequest mReq;
						mReq = cred.prep(children(item));
						mReq.status = REQ_NEW;
						mReq.save(db);
					} else {
						APIRequest mReq;
						for (String key : missing) {
							mReq = fetchItem(key, cred);
							mReq.status = REQ_NEW;
							mReq.save(db);
						}
					}
				}
			} else if ("json".equals(disposition)) {
			} else {
			}
			getHandler().onComplete(this);
		}
	}
