htiextension/com.nokia.s60tools.hticonnection/src/com/nokia/s60tools/hticonnection/core/RequestQueueManager.java
changeset 0 61163b28edca
--- /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.
+	}
+}