Java tutorial
/* * Copyright 2012 software2012team23 * * 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 at.tugraz.ist.akm.webservice.requestprocessor; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; import my.org.apache.http.HttpException; import my.org.apache.http.HttpResponse; import my.org.apache.http.ParseException; import my.org.apache.http.RequestLine; import my.org.apache.http.protocol.HttpContext; import my.org.apache.http.protocol.HttpRequestHandlerRegistry; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.content.Context; import at.tugraz.ist.akm.content.SmsContentConstants; import at.tugraz.ist.akm.content.query.ContactFilter; import at.tugraz.ist.akm.content.query.TextMessageFilter; import at.tugraz.ist.akm.io.xml.XmlNode; import at.tugraz.ist.akm.monitoring.BatteryStatus; import at.tugraz.ist.akm.monitoring.SystemMonitor; import at.tugraz.ist.akm.monitoring.TelephonySignalStrength; import at.tugraz.ist.akm.phonebook.contact.Contact; import at.tugraz.ist.akm.phonebook.contact.IContactModifiedCallback; import at.tugraz.ist.akm.sms.ISmsIOCallback; import at.tugraz.ist.akm.sms.TextMessage; import at.tugraz.ist.akm.texting.TextingAdapter; import at.tugraz.ist.akm.texting.TextingInterface; import at.tugraz.ist.akm.trace.LogClient; import at.tugraz.ist.akm.webservice.WebServerConstants; import at.tugraz.ist.akm.webservice.protocol.json.JsonFactory; public class JsonAPIRequestProcessor extends AbstractHttpRequestProcessor implements ISmsIOCallback, IContactModifiedCallback { private final static String JSON_STATE_SUCCESS = "success"; private final static String JSON_STATE_ERROR = "error"; private final LogClient mLog = new LogClient(this); private JsonFactory mJsonFactory = new JsonFactory(); private volatile TextingInterface mTextingAdapter; private volatile SystemMonitor mSystemMonitor; private int mSMSThreadMessageCount; /** members to represent a state */ private volatile boolean mSMSSentSuccess = false; private volatile boolean mContactsChanged = false; private volatile boolean mSMSReceived = false; private volatile boolean mSMSSentError = false; private volatile HashMap<String, Integer> mSMSWaitingForSentCallback = new HashMap<String, Integer>(); private volatile List<TextMessage> mSMSSentList = new ArrayList<TextMessage>(); private volatile List<TextMessage> mSMSReceivedList = new ArrayList<TextMessage>(); private volatile List<TextMessage> mSMSSentErrorList = new ArrayList<TextMessage>(); private volatile JSONArray mJsonContactList = null; private Object mJsonContactListLock = new Object(); private ISmsIOCallback mExternalSMSIoCallback = null; public synchronized void registerSMSIoListener(ISmsIOCallback smsListener) { mExternalSMSIoCallback = smsListener; } public synchronized void unregisterSMSIoListener() { mExternalSMSIoCallback = null; } public JsonAPIRequestProcessor(final Context context, final XmlNode config, final HttpRequestHandlerRegistry registry) throws Throwable { super(context, config, registry); mTextingAdapter = new TextingAdapter(context, this, this); mLog.debug("preloading contacts [start]"); mJsonContactList = fetchContactsJsonArray(); mLog.debug("preloading contacts [done]"); mSystemMonitor = new SystemMonitor(context); mSMSThreadMessageCount = 20; mSystemMonitor.start(); mTextingAdapter.start(); } private JsonAPIRequestProcessor() { super(null, null, null); } @Override synchronized public void handleRequest(RequestLine requestLine, String requestData, HttpResponse httpResponse, HttpContext httpContext) throws HttpException, IOException { if (requestLine.getMethod().equals(WebServerConstants.HTTP.REQUEST_TYPE_POST)) { JSONObject json; try { json = new JSONObject(requestData); String method = json.getString(WebServerConstants.JSON.METHOD); if (method != null && method.length() > 0) { JSONArray jsonParams = null; if (json.isNull(WebServerConstants.JSON.PARAMS) == false) { jsonParams = json.getJSONArray(WebServerConstants.JSON.PARAMS); } JSONObject jsonResponse = processMethod(method, jsonParams); mResponseDataAppender.appendHttpResponseData(httpResponse, jsonResponse); } else { mLog.error("no method defined in JSON post request ==> <" + json.toString() + ">"); return; } } catch (ParseException parseException) { parseException.printStackTrace(); } catch (JSONException jsonException) { jsonException.printStackTrace(); } } } @Override public synchronized void close() throws IOException { mTextingAdapter.stop(); try { mTextingAdapter.close(); } catch (IOException e) { mLog.error("failed closing texting adapter"); } mSystemMonitor.stop(); try { mSystemMonitor.close(); } catch (Throwable f) { mLog.error("failed closing app monitor"); } mJsonFactory = null; mTextingAdapter = null; mSystemMonitor = null; mSMSWaitingForSentCallback = null; mSMSSentList = null; mSMSReceivedList = null; mSMSSentErrorList = null; mJsonContactList = null; mJsonContactListLock = null; mExternalSMSIoCallback = null; super.close(); } @Override public void contactModifiedCallback() { mLog.debug("reloading all contacts from provider"); synchronized (mJsonContactListLock) { mJsonContactList = fetchContactsJsonArray(); this.mContactsChanged = true; } } @Override public synchronized void smsSentCallback(Context context, List<TextMessage> messages) { if (messages.isEmpty() == false) { for (TextMessage message : messages) { String address = message.getAddress(); mLog.debug("looking for address in waiting queue with :" + address); if (mSMSWaitingForSentCallback.containsKey(address)) { int tmpCount = mSMSWaitingForSentCallback.get(address); tmpCount = tmpCount - 1; // if we received all callbacks for an specific address we // can // assume, that the sms was sent successfully and the count // is 0 if (tmpCount == 0) { mSMSSentSuccess = true; mSMSSentList.add(message); mSMSWaitingForSentCallback.remove(address); mLog.debug("received all sms callbacks for address " + address + " going to notify webapp"); } else { mSMSWaitingForSentCallback.put(address, tmpCount); mLog.debug("received sms callback for address " + address + " - count is: " + tmpCount); } } else { mLog.error( "Got a callback for address " + address + " but could not be found in waiting list!"); } } } else { mLog.warning("A sms sent callback was delivered but textmessages list was empty"); } if (null != mExternalSMSIoCallback) { mExternalSMSIoCallback.smsSentCallback(context, messages); } } @Override public synchronized void smsSentErrorCallback(Context context, List<TextMessage> messages) { this.mSMSSentError = true; for (TextMessage message : messages) { this.mSMSSentErrorList.add(message); } if (null != mExternalSMSIoCallback) { mExternalSMSIoCallback.smsSentErrorCallback(context, messages); } } @Override public synchronized void smsDeliveredCallback(Context context, List<TextMessage> message) { if (null != mExternalSMSIoCallback) { mExternalSMSIoCallback.smsDeliveredCallback(context, message); } } @Override public synchronized void smsReceivedCallback(Context context, List<TextMessage> messages) { this.mSMSReceived = true; for (TextMessage message : messages) { mLog.debug("textmessage from " + message.getAddress() + " received in api request handler."); this.mSMSReceivedList.add(message); } if (null != mExternalSMSIoCallback) { mExternalSMSIoCallback.smsReceivedCallback(context, messages); } } private JSONObject processMethod(String method, JSONArray jsonParams) { mLog.debug("handle api request <" + method + ">"); JSONObject resultObject = new JSONObject(); if (jsonParams == null) { mLog.debug("0 parameter found for request <" + method + ">"); } else { mLog.debug("parameters " + jsonParams.toString()); } try { if (method.compareTo("get_contacts") == 0) { resultObject = this.getContacts(); } else if (method.compareTo("send_sms_message") == 0) { resultObject = this.sendSMS(jsonParams); } else if (method.compareTo("info") == 0) { resultObject = this.createPollingInfo(); } else if (method.compareTo("fetch_sms_thread") == 0) { resultObject = this.fetchSMSThread(jsonParams); } else { String logMsg = "No method found for given request method: " + method; mLog.warning(logMsg); resultObject.put("state", JSON_STATE_ERROR); resultObject.put("error_msg", logMsg); } } catch (JSONException jsonException) { mLog.error("Could not create jsonobject for handling api request.", jsonException); } return resultObject; } private static class TextMessageThreadSort implements Comparator<TextMessage> { @Override public int compare(TextMessage message1, TextMessage message2) { Date date1 = new Date(Long.parseLong(message1.getDate())); Date date2 = new Date(Long.parseLong(message2.getDate())); return (-1) * (date1.compareTo(date2)); } } private synchronized JSONObject fetchSMSThread(JSONArray params) { JSONObject resultObject = new JSONObject(); String contact_id = ""; int paramsLength = params.length(); int messageCount = this.mSMSThreadMessageCount; try { if (paramsLength == 1) { List<TextMessage> threadList = new ArrayList<TextMessage>(); JSONObject jsonParams = params.getJSONObject(0); contact_id = jsonParams.getString("contact_id"); mLog.debug("fetch SMS thread with given contact_id [" + contact_id + "]"); ContactFilter conFilter = new ContactFilter(); conFilter.setId(Integer.parseInt(contact_id)); List<Contact> contacts = mTextingAdapter.fetchContacts(conFilter); mLog.debug("found contacts - list size [" + contacts.size() + "]"); if (contacts.size() == 1) { Contact contact = contacts.get(0); List<Contact.Number> phoneNumbers = contact.getPhoneNumbers(); for (Contact.Number entry : phoneNumbers) { if (messageCount <= 0) { break; } mLog.debug("fetch SMS thread of [" + contact.getDisplayName() + "] with number [" + entry.getNumber() + "]"); List<Integer> threadIds = mTextingAdapter.fetchThreadIds(entry.getNumber()); for (Integer threadId : threadIds) { if (messageCount <= 0) { break; } TextMessageFilter msgFilter = new TextMessageFilter(); msgFilter.setThreadId(threadId.longValue()); msgFilter.setBox(SmsContentConstants.Uri.BASE_URI); mLog.debug("fetch SMS thread with ID [" + threadId + "]"); List<TextMessage> threadMessages = mTextingAdapter.fetchTextMessages(msgFilter); for (TextMessage msg : threadMessages) { if (messageCount <= 0) { break; } threadList.add(msg); messageCount--; } } } // sort the list by date Collections.sort(threadList, new TextMessageThreadSort()); JSONArray thread_messages = new JSONArray(); for (TextMessage msg : threadList) { JSONObject entry = new JSONObject(); if (msg.getPerson() != null && msg.getPerson().compareTo("null") != 0 && msg.getPerson().length() > 0) { entry.put("real_contact_id", contact_id); } else { entry.put("real_contact_id", ""); } entry.put("message", mJsonFactory.createJsonObject(msg)); thread_messages.put(entry); } setSuccessState(resultObject); resultObject.put("thread_messages", thread_messages); resultObject.put("contact_id", contact_id); } else { mLog.warning("contact with id [" + contact_id + "] not found or ambiguous"); this.setErrorState(resultObject, "contact not found"); return resultObject; } } else { this.setErrorState(resultObject, "Corrupt amount of parameters given to fetch sms tread."); return resultObject; } } catch (JSONException jsonException) { mLog.error("Parameters for fetching sms thread could not be extracted from the given api request.", jsonException); } return resultObject; } private void setSuccessState(JSONObject obj) { try { obj.put("state", JSON_STATE_SUCCESS); } catch (JSONException jsonException) { jsonException.printStackTrace(); } } private void setErrorState(JSONObject obj, String msg) { try { obj.put("state", JSON_STATE_ERROR); obj.put("error_msg", msg); } catch (JSONException jsonException) { jsonException.printStackTrace(); } } private JSONObject getContacts() { synchronized (mJsonContactListLock) { mLog.debug("handle get_contacts request"); JSONObject resultObject = new JSONObject(); try { resultObject.put("contacts", mJsonContactList); } catch (JSONException jsonException) { mLog.error("Could not append contact list to json object.", jsonException); return new JSONObject(); } if (resultObject.length() > 0) { this.setSuccessState(resultObject); } else { this.setErrorState(resultObject, "No contacts could be found on the device."); } return resultObject; } } private synchronized JSONArray fetchContactsJsonArray() { mLog.debug("fetch contacts from provider [start]"); ContactFilter allFilter = new ContactFilter(); allFilter.setWithPhone(true); allFilter.setOrderByDisplayName(true, ContactFilter.SORT_ORDER_ASCENDING); List<Contact> contacts = mTextingAdapter.fetchContacts(allFilter); JSONArray contactList = new JSONArray(); while (contacts.size() > 0) { contactList.put(mJsonFactory.createJsonObject(contacts.get(0))); contacts.remove(0); } mLog.debug("fetch " + contacts.size() + " contacts from provider [done]"); return contactList; } private synchronized JSONObject sendSMS(JSONArray params) { JSONObject resultObject = new JSONObject(); String address = ""; String message = ""; String exceptionalMessage = "Parameters for sending sms could not be fetched from the given api request."; int paramsLength = params.length(); try { if (paramsLength == 1) { JSONObject jsonParams = params.getJSONObject(0); address = jsonParams.getString("address"); message = URLDecoder.decode(jsonParams.getString("message"), "UTF-8"); mLog.debug("fetch parameter adress [" + address + "] and message [" + message + "] from send sms request"); } else { this.setErrorState(resultObject, "Corrupt amount of parameters given to send sms."); return resultObject; } } catch (JSONException e) { mLog.error(exceptionalMessage, e); } catch (UnsupportedEncodingException e) { mLog.error(exceptionalMessage, e); } if (address.length() > 0 && message.length() > 0) { TextMessage sentMessage = new TextMessage(); sentMessage.setAddress(address); sentMessage.setBody(message); int parts = mTextingAdapter.sendTextMessage(sentMessage); // store the part count or arrange an already set count for this // specific address if (this.mSMSWaitingForSentCallback.containsKey(address)) { int tmpCount = this.mSMSWaitingForSentCallback.get(address); this.mSMSWaitingForSentCallback.put(address, (tmpCount + parts)); } else { this.mSMSWaitingForSentCallback.put(address, parts); } mLog.debug("message queued for sending to address " + address + "] and parts [" + parts + "]"); this.setSuccessState(resultObject); } else { this.setErrorState(resultObject, "One or all of the given parameters are empty or corrupt."); } return resultObject; } /** * Provide the following informations: --> Update of the statusbar --> * Inform about contact changes and if true send whole contacts back. --> * Inform about successfully sent sms and if true array of address which has * been sent --> Inform about received sms and if true the return the * textmessages as json * * @return */ private synchronized JSONObject createPollingInfo() { JSONObject result = new JSONObject(); try { mLog.debug("evaluate contact changed state"); result.put("contact_changed", this.mContactsChanged); if (this.mContactsChanged) { result.put("contacts", this.mJsonContactList); this.mContactsChanged = false; } mLog.debug("evaluate sms sent error"); result.put("sms_sent_error", this.mSMSSentError); if (this.mSMSSentError) { JSONArray errorList = new JSONArray(); for (int idx = 0; idx < mSMSSentErrorList.size(); idx++) { errorList.put(mJsonFactory.createJsonObject(mSMSSentErrorList.get(idx))); } result.put("sms_sent_error_messages", errorList); this.mSMSSentError = false; } mLog.debug("evaluate sms sent success"); result.put("sms_sent_success", this.mSMSSentSuccess); if (this.mSMSSentSuccess) { JSONArray sentList = new JSONArray(); for (int idx = 0; idx < mSMSSentList.size(); idx++) { sentList.put(mJsonFactory.createJsonObject(mSMSSentList.get(idx))); } result.put("sms_sent_success_messages", sentList); this.mSMSSentSuccess = false; } mLog.debug("evaluate sms received"); result.put("sms_received", this.mSMSReceived); if (this.mSMSReceived) { JSONArray recvList = new JSONArray(); for (int idx = 0; idx < mSMSReceivedList.size(); idx++) { recvList.put(mJsonFactory.createJsonObject(mSMSReceivedList.get(idx))); } result.put("sms_received_messages", recvList); this.mSMSReceived = false; } mLog.debug("clear temporary member lists"); this.mSMSSentErrorList.clear(); this.mSMSSentList.clear(); this.mSMSReceivedList.clear(); mLog.debug("evaluate current telephone state"); BatteryStatus batteryStatus = this.mSystemMonitor.getBatteryStatus(); TelephonySignalStrength telSignalStrength = this.mSystemMonitor.getTelephonySignalStrength(); if (batteryStatus != null) { result.put("battery", mJsonFactory.createJsonObject(batteryStatus)); try { batteryStatus.close(); } catch (Throwable e) { mLog.error("failed closing battery status"); } } if (telSignalStrength != null) { result.put("signal", mJsonFactory.createJsonObject(telSignalStrength)); try { telSignalStrength.close(); } catch (Throwable e) { mLog.error("failed closing telephony signal strength"); } } this.setSuccessState(result); } catch (JSONException jsonException) { mLog.error("Could not create the polling json object", jsonException); this.setErrorState(result, "Could not create polling object"); } return result; } }