/* * CDDL HEADER START * * The contents of this file are subject to the terms of the Common Development * and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at * src/com/vodafone360/people/VODAFONE.LICENSE.txt or * http://github.com/360/360-Engine-for-Android * See the License for the specific language governing permissions and * limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each file and * include the License file at src/com/vodafone360/people/VODAFONE.LICENSE.txt. * If applicable, add the following below this CDDL HEADER, with the fields * enclosed by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * Copyright 2010 Vodafone Sales & Services Ltd. All rights reserved. * Use is subject to license terms. */ package com.vodafone360.people.engine.content; import java.util.Hashtable; import java.util.List; import com.vodafone360.people.database.DatabaseHelper; import com.vodafone360.people.datatypes.BaseDataType; import com.vodafone360.people.datatypes.ExternalResponseObject; import com.vodafone360.people.engine.BaseEngine; import com.vodafone360.people.engine.IEngineEventCallback; import com.vodafone360.people.engine.EngineManager.EngineId; import com.vodafone360.people.service.ServiceUiRequest; import com.vodafone360.people.service.io.QueueManager; import com.vodafone360.people.service.io.Request; import com.vodafone360.people.service.io.ResponseQueue.DecodedResponse; /** * Content engine for downloading and uploading all kind of content (pictures, * videos, files) */ public class ContentEngine extends BaseEngine { /** * The amount of time in milliseconds that the engine is allowed to run. */ private final static int ALLOWED_RUNNING_TIME_MS = 150; /** * Constructor for the ContentEngine. * * @param eventCallback IEngineEventCallback for calling the constructor of * the super class * @param dbHelper Instance of DatabaseHelper */ public ContentEngine(final IEngineEventCallback eventCallback, final DatabaseHelper dbHelper) { super(eventCallback); this.mDbHelper = dbHelper; mEngineId = EngineId.CONTENT_ENGINE; } /** * Queue with unprocessed ContentObjects. */ private FiFoQueue mUnprocessedQueue = new FiFoQueue(); /** * Queue with ContentObjects for downloads. */ private FiFoQueue mDownloadQueue = new FiFoQueue(); /** * Queue with ContentObjects for uploads. */ private FiFoQueue mUploadQueue = new FiFoQueue(); /** * Instance of DatabaseHelper. */ private DatabaseHelper mDbHelper; /** * Hashtable to match requests to ContentObjects. */ private Hashtable<Integer, ContentObject> requestContentObjectMatchTable = new Hashtable<Integer, ContentObject>(); /** * Getter for the local instance of DatabaseHelper. * * @return local instance of DatabaseHelper */ public final DatabaseHelper getDatabaseHelper() { return mDbHelper; } /** * Processes one ContentObject. * * @param co ContentObject to be processed */ public final void processContentObject(final ContentObject co) { mUnprocessedQueue.add(co); } /** * Iterates over the ContentObject list and processes every element. * * @param list List with ContentObjects which are to be processed */ public final void processContentObjects(final List<ContentObject> list) { for (ContentObject co : list) { processContentObject(co); } } /** * Processes the main queue and splits it into the download and upload * queues. */ private void processQueue() { ContentObject co; // picking unprocessed ContentObjects while ((co = mUnprocessedQueue.poll()) != null) { // putting them to downloadqueue ... if (co.getDirection() == ContentObject.TransferDirection.DOWNLOAD) { mDownloadQueue.add(co); } else { // ... or the uploadqueue mUploadQueue.add(co); } } } /** * Determines the next RunTime of this Engine It first processes the * in-queue and then look. * * @return time in milliseconds from now when the engine should be run */ @Override public final long getNextRunTime() { processQueue(); // if there are CommsResponses outstanding, run now if (isCommsResponseOutstanding()) { return 0; } return (mDownloadQueue.size() + mUploadQueue.size() > 0) ? 0 : -1; } /** * Empty implementation without function at the moment. */ @Override public void onCreate() { } /** * Empty implementation without function at the moment. */ @Override public void onDestroy() { } /** * Empty implementation without function at the moment. */ @Override protected void onRequestComplete() { } /** * Empty implementation without function at the moment. */ @Override protected void onTimeoutEvent() { } /** * Processes the response Finds the matching contentobject for the repsonse * using the id of the response and sets its status to done. At last the * TransferComplete method of the ContentObject is called. * * @param resp Response object that has been processed */ @Override protected final void processCommsResponse(final DecodedResponse resp) { ContentObject co = requestContentObjectMatchTable.remove(resp.mReqId); if (co == null) { // check if we have an invalid response return; } List<BaseDataType> mDataTypes = resp.mDataTypes; // Sometimes it is null or empty if (mDataTypes == null || mDataTypes.size()==0) { co.setTransferStatus(ContentObject.TransferStatus.ERROR); RuntimeException exc = new RuntimeException("Empty response returned"); co.getTransferListener().transferError(co, exc); return; } Object data = mDataTypes.get(0); if (mDataTypes.get(0).getType() == BaseDataType.SERVER_ERROR_DATA_TYPE || mDataTypes.get(0).getType() == BaseDataType.SYSTEM_NOTIFICATION_DATA_TYPE) { co.setTransferStatus(ContentObject.TransferStatus.ERROR); RuntimeException exc = new RuntimeException(data.toString()); co.getTransferListener().transferError(co, exc); } else { co.setTransferStatus(ContentObject.TransferStatus.DONE); co.setExtResponse((ExternalResponseObject) data); co.getTransferListener().transferComplete(co); } } /** * Empty implementation of abstract method from BaseEngine. */ @Override protected void processUiRequest(final ServiceUiRequest requestId, final Object data) { } /** * run method of this engine iterates over the downloadqueue, makes requests * out of ContentObjects and puts them into QueueManager queue. */ @Override public final void run() { // set it to true so at least one response is treated per call to run() boolean carryOn = true; final long runStartTime = System.currentTimeMillis(); while (isCommsResponseOutstanding() && carryOn) { // process as many responses as we can during the allowed time slot processCommsInQueue(); carryOn = System.currentTimeMillis() - runStartTime < ALLOWED_RUNNING_TIME_MS; } // do not send more requests if we are still struggling with // outstanding responses if (isCommsResponseOutstanding()) return; ContentObject co; boolean queueChanged = false; while ((co = mDownloadQueue.poll()) != null) { queueChanged = true; // set the status of this contentobject to transferring co.setTransferStatus(ContentObject.TransferStatus.TRANSFERRING); Request request = new Request(co.getUrl().toString(), co.getUrlParams(), engineId()); QueueManager.getInstance().addRequest(request); // important: later we will match done requests back to the // contentobject using this map requestContentObjectMatchTable.put(request.getRequestId(), co); } if (queueChanged) { QueueManager.getInstance().fireQueueStateChanged(); } } @Override public void onReset() { super.onReset(); requestContentObjectMatchTable.clear(); mUnprocessedQueue.clear(); mDownloadQueue.clear(); mUploadQueue.clear(); } }