htiextension/com.nokia.s60tools.hticonnection/src/com/nokia/s60tools/hticonnection/core/RequestQueueManager.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/htiextension/com.nokia.s60tools.hticonnection/src/com/nokia/s60tools/hticonnection/core/RequestQueueManager.java Tue Jan 12 13:17:53 2010 -0600
@@ -0,0 +1,334 @@
+/*
+* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package com.nokia.s60tools.hticonnection.core;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+
+import com.nokia.s60tools.hticonnection.core.HtiConnection.ConnectionStatus;
+import com.nokia.s60tools.hticonnection.exceptions.ConnectionException;
+import com.nokia.s60tools.hticonnection.exceptions.HTIException;
+import com.nokia.s60tools.hticonnection.exceptions.ServiceShutdownException;
+import com.nokia.s60tools.hticonnection.preferences.HtiApiPreferencePage;
+import com.nokia.s60tools.hticonnection.resources.Messages;
+import com.nokia.s60tools.hticonnection.services.HTIVersion;
+import com.nokia.s60tools.hticonnection.services.connectiontestservice.GetVersionRequest;
+import com.nokia.s60tools.hticonnection.services.ftpservice.AbstractFileTransferRequest;
+import com.nokia.s60tools.util.console.IConsolePrintUtility;
+
+/**
+ * Singleton class that handles messages one at the time.
+ */
+public class RequestQueueManager {
+
+ /**
+ * Service that holds requests in line and handles them.
+ */
+ private ExecutorService executorService;
+ /**
+ * Instance of this singleton class.
+ */
+ private static RequestQueueManager instance = null;
+
+ /**
+ * List of requests that are currently in queue or executing.
+ */
+ private static List<AbstractRequest> requests;
+
+ /**
+ * Private constructor to prevent creating new instances.
+ */
+ private RequestQueueManager(){
+ executorService = Executors.newSingleThreadExecutor();
+ }
+
+ /**
+ * Only one instance can exist at one time.
+ * @return Current instance.
+ */
+ public static synchronized RequestQueueManager getInstance(){
+ if( instance == null ){
+ instance = new RequestQueueManager();
+ requests = Collections.synchronizedList(new ArrayList<AbstractRequest>());
+ }
+ return instance;
+ }
+
+ /**
+ * Resets manager so that it can be used again after stop if needed.
+ */
+ public void reset(){
+ instance = null;
+ }
+
+ /**
+ * ExecutorService needs to be shutdown, so that it won't stay active.
+ * This should be run after there won't be new tasks coming.
+ */
+ @SuppressWarnings("unchecked")
+ public void stop(){
+ // Shutting down all
+ List<Runnable> requests = executorService.shutdownNow();
+
+ for(Runnable runnable : requests) {
+ if(runnable instanceof FutureTask) {
+ // Warning from this unchecked cast is suppressed as it doesn't
+ // matter what type of FutureTask it is. Just canceling it.
+ FutureTask request = (FutureTask)runnable;
+ request.cancel(false);
+ }
+ }
+
+ try {
+ // Waiting for commands to complete for one second and then shutting down..
+ executorService.awaitTermination(100, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ // Service is being shutdown. Exception doesn't matter anymore.
+ }
+ }
+
+ /**
+ * Submits request to ExecutorService and returns result for request.
+ * @param requestCallable Request that will be added to queue.
+ * @param printUtility Used for printing messages.
+ * @return RequestResult contains request.
+ * @throws ConnectionException Connection failed.
+ * @throws HTIException Thrown when connection HTI agent could not be initialized.
+ * @throws ServiceShutdownException Thrown if services have been shut down
+ */
+ public RequestResult submit(AbstractRequest requestCallable, IConsolePrintUtility printUtility)
+ throws HTIException, ConnectionException, ServiceShutdownException {
+ // Checking if services are shutdown.
+ ConnectionStatus status = HtiConnection.getInstance().getConnectionStatus();
+ if(executorService.isShutdown() || status != ConnectionStatus.CONNECTED){
+ throw new ServiceShutdownException(Messages.getString("RequestQueueManager.ServiceShutdown_Exception_Msg")); //$NON-NLS-1$
+ }
+
+ // Creating future task that holds request.
+ FutureTask<RequestResult> resultFutureTask = new FutureTask<RequestResult>(requestCallable);
+ requestCallable.setFutureTask(resultFutureTask);
+
+ // Handling FTP Request before putting them in to queue.
+ if(requestCallable instanceof AbstractFileTransferRequest) {
+ AbstractFileTransferRequest ftpRequest = (AbstractFileTransferRequest) requestCallable;
+ ftpRequest.informInQueue();
+ }
+
+ // Submitting the task to executor service.
+ executorService.submit(resultFutureTask);
+ return getResult(requestCallable, resultFutureTask, printUtility);
+
+ }
+
+ /**
+ * Returns result from resultFutureTasks.
+ * @param requestCallable Request that will be added to queue.
+ * @param resultFutureTask Future task that is created from requestCallable.
+ * @param printUtility printUtility Used for printing messages.
+ * @return RequestResult contains request.
+ * @throws ConnectionException Connection failed.
+ * @throws HTIException Thrown when connection HTI agent could not be initialized.
+ * @throws ServiceShutdownException Thrown if services have been shut down
+ */
+ private RequestResult getResult(AbstractRequest requestCallable,
+ FutureTask<RequestResult> resultFutureTask,
+ IConsolePrintUtility printUtility)
+ throws HTIException, ConnectionException, ServiceShutdownException {
+
+ // Keeping requests in list, so that they can be canceled if necessary.
+ synchronized(requests) {
+ requests.add(requestCallable);
+ }
+
+ RequestResult result = null;
+ try {
+ // Get method blocks the call, until computation of the result is finished or exception has arisen.
+ result = resultFutureTask.get();
+ } catch (CancellationException e) {
+ if(HtiConnection.getInstance().getConnectionStatus() != ConnectionStatus.CONNECTED) {
+ // Not connected to service.
+ throw new ServiceShutdownException(Messages.getString("RequestQueueManager.Datagateway_Shutdown_Exception_Msg")); //$NON-NLS-1$
+ }
+
+ // Request was canceled. Informing caller.
+ throw new HTIException(requestCallable.getRequestName()
+ + Messages.getString("RequestQueueManager.RequestCanceled_Exception_Msg0"), true, true); //$NON-NLS-1$
+ } catch (InterruptedException e) {
+ handleInterrupedException(requestCallable, e, printUtility);
+ } catch (ExecutionException e) {
+ handleExecutionException(requestCallable, e, printUtility);
+ } finally {
+ // Removing requests that have been already executed.
+ synchronized(requests) {
+ requests.remove(requestCallable);
+ }
+
+ if(requestCallable instanceof AbstractFileTransferRequest) {
+ // Handling informing about the end of the FTP Request.
+ ((AbstractFileTransferRequest) requestCallable).informEnded();
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Tests connection by sending get version request to the device.
+ * This method is used to test connection when it is in testing state.
+ * Throws exceptions which have occurred when getting the response.
+ * @return True if connection is up and running. False if request failed to
+ * get data or connection is down.
+ * @throws InterruptedException if the request was interrupted while waiting.
+ * @throws ExecutionException if the request threw an exception.
+ */
+ public boolean testConnection() throws InterruptedException, ExecutionException {
+
+ ConnectionStatus status = HtiConnection.getInstance().getConnectionStatus();
+
+ // Checking if service is shutdown.
+ if(status == ConnectionStatus.SHUTDOWN || executorService.isShutdown() ){
+ return false;
+ }
+
+ GetVersionRequest request = new GetVersionRequest();
+
+ // Creating future task that holds request.
+ FutureTask<RequestResult> resultFutureTask = new FutureTask<RequestResult>(request);
+
+ // Submitting the task to executor service.
+ executorService.submit(resultFutureTask);
+
+ // Get method blocks the call, until computation of the result is finished or exception has arisen.
+ RequestResult result = resultFutureTask.get();
+ HTIVersion version = result.getHTIVersionData();
+
+ if (version != null) {
+ // Request succeeded
+ HtiConnection.getInstance().setHTIVersion(version);
+ return true;
+ }
+
+ // Request didn't return value.
+ return false;
+ }
+
+ /**
+ * Cancels all requests that are currently in queue.
+ */
+ public void cancelRequestsInQueue() {
+ synchronized(requests) {
+ Iterator<AbstractRequest> iter = requests.iterator();
+ while(iter.hasNext()) {
+ iter.next().cancel();
+ }
+ }
+ }
+
+ /**
+ * Handling executionExceptions. Logging errors and throwing correct exception.
+ * @param requestCallable Request that caused the exception.
+ * @param exception Exception to be handled.
+ * @param printUtility Used for printing messages.
+ * @throws ConnectionException Thrown if connection failed.
+ * @throws HTIException Thrown if couldn't connect to HTI agent.
+ * @throws ServiceShutdownException Thrown if services have been shut down.
+ */
+ private void handleExecutionException(AbstractRequest requestCallable, ExecutionException exception,
+ IConsolePrintUtility printUtility)
+ throws ConnectionException, HTIException, ServiceShutdownException {
+ // Testing if service has been shutdown.
+ if(HtiConnection.getInstance().getConnectionStatus() != ConnectionStatus.CONNECTED){
+ throw new ServiceShutdownException(Messages.getString("RequestQueueManager.Datagateway_Shutdown_Exception_Msg")); //$NON-NLS-1$
+ }
+
+ // Handling exceptions arisen when running call.
+ printUtility.println(requestCallable.getRequestName() +
+ Messages.getString("RequestQueueManager.RequestFailed_Exception_Msg") + //$NON-NLS-1$
+ exception.getCause().getMessage(),
+ IConsolePrintUtility.MSG_ERROR);
+
+ handleConnectionFailure(requestCallable, exception, printUtility);
+
+ // Checking if problem was that HTI agent couldn't be initialized.
+ if (exception.getMessage().equals("com.nokia.HTI.HTIException: HTI NOT INITIALIZED")) { //$NON-NLS-1$
+ throw new HTIException(exception.getMessage(), false);
+ }
+
+ throw new ConnectionException(exception.getMessage());
+ }
+
+ /**
+ * Handling executionExceptions. Logging errors and throwing correct exception.
+ * @param requestCallable Request that caused the exception.
+ * @param exception exception Exception to be handled.
+ * @param printUtility Used for printing messages.
+ * @throws ConnectionException Thrown if connection failed.
+ * @throws ServiceShutdownException Thrown if services have been shut down.
+ */
+ private void handleInterrupedException(AbstractRequest requestCallable,
+ InterruptedException exception, IConsolePrintUtility printUtility)
+ throws ConnectionException, ServiceShutdownException {
+ // Testing if service has been shutdown.
+ if(HtiConnection.getInstance().getConnectionStatus() != ConnectionStatus.CONNECTED){
+ throw new ServiceShutdownException(Messages.getString("RequestQueueManager.Datagateway_Shutdown_Exception_Msg")); //$NON-NLS-1$
+ }
+
+ // Handling interrupts in thread handling.
+ printUtility.println(requestCallable.getRequestName() +
+ Messages.getString("RequestQueueManager.RequestInterrupted_Exception_Msg") + //$NON-NLS-1$
+ exception.getCause().getMessage(),
+ IConsolePrintUtility.MSG_ERROR);
+ throw new ConnectionException(exception.getMessage());
+ }
+
+ /**
+ * Doing necessary actions when connection failure happens.
+ * Informs user when connection/request fails because of connection problems
+ * and offers possibility to change/test connection settings.
+ * @param requestCallable Request that caused the exception.
+ * @param exception Exception to be handled.
+ * @param printUtility Used for printing messages.
+ */
+ private synchronized void handleConnectionFailure(final AbstractRequest requestCallable,
+ ExecutionException exception, IConsolePrintUtility printUtility)
+ throws ServiceShutdownException{
+ // Showing error message only if connection is up and preferences page is not open.
+ if(HtiConnection.getInstance().getConnectionStatus() != ConnectionStatus.CONNECTED
+ && HtiApiPreferencePage.isCreated()){
+ return;
+ }
+
+ boolean isReported = HtiConnection.getInstance().reportConnectionError(exception);
+
+ if(isReported) {
+ // Error reported and service is now in CONNECTING state because of connection problem.
+ throw new ServiceShutdownException(Messages.getString("RequestQueueManager.Datagateway_Shutdown_Exception_Msg")); //$NON-NLS-1$
+ }
+
+ // It is now checked that HTI was initialized correctly and connection was established.
+ // Leaving handling of other problems to the client.
+ }
+}