/*==========================================================================*\
| $Id: PreviewingResultJob.java,v 1.1 2010/05/11 15:52:46 aallowat Exp $
|*-------------------------------------------------------------------------*|
| Copyright (C) 2006-2008 Virginia Tech
|
| This file is part of Web-CAT.
|
| Web-CAT is free software; you can redistribute it and/or modify
| it under the terms of the GNU Affero General Public License as published
| by the Free Software Foundation; either version 3 of the License, or
| (at your option) any later version.
|
| Web-CAT is distributed in the hope that it will be useful,
| but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
| GNU General Public License for more details.
|
| You should have received a copy of the GNU Affero General Public License
| along with Web-CAT; if not, see <http://www.gnu.org/licenses/>.
\*==========================================================================*/
package org.webcat.oda.designer.preview;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Hashtable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.webcat.oda.designer.DesignerActivator;
import org.webcat.oda.designer.i18n.Messages;
import org.webcat.oda.designer.util.WOActionDispatcher;
import org.webcat.oda.designer.util.WOActionResponse;
//------------------------------------------------------------------------
/**
* TODO: real description
*
* @author Tony Allevato (Virginia Tech Computer Science)
* @version $Id: PreviewingResultJob.java,v 1.1 2010/05/11 15:52:46 aallowat Exp $
*/
public class PreviewingResultJob extends Job
{
// ------------------------------------------------------------------------
/**
*
* @param jobName
* @param url
* @param username
* @param password
* @param maxRecords
* @param timeout
* @param dataSetUuid
* @param entityType
* @param expressions
*/
public PreviewingResultJob(String jobName, String url, String username,
String password, int maxRecords, int timeout, String dataSetUuid,
String entityType, String[] expressions)
{
super(jobName);
this.serverUrl = url;
this.username = username;
this.password = password;
this.maxRecords = maxRecords;
this.timeout = timeout;
this.dataSetUuid = dataSetUuid;
this.entityType = entityType;
this.expressions = expressions;
StringBuilder builder = new StringBuilder();
builder.append(expressions[0]);
for (int i = 1; i < expressions.length; i++)
{
builder.append("%===%");
builder.append(expressions[i]);
}
this.expressionParameterString = builder.toString();
currentBatchRetrieved = false;
currentBatchLock = new Object();
}
// ------------------------------------------------------------------------
/**
*
*/
@Override
protected IStatus run(IProgressMonitor monitor)
{
monitor.beginTask(Messages.PREVIEW_JOB_OBTAINING_DATA,
maxRecords);
IStatus status = performRetrieval(monitor);
if (status.isOK() && retrievedRows != null)
{
DesignerActivator.getDefault().getPreviewCache().movePendingToCached(
dataSetUuid, retrievedRows);
}
else
{
DesignerActivator.getDefault().getPreviewCache().cancelPending(
dataSetUuid);
}
monitor.done();
return status;
}
private IStatus performRetrieval(IProgressMonitor monitor)
{
IStatus status = Status.OK_STATUS;
long startTime = System.currentTimeMillis();
long endTime = startTime + (timeout * 1000);
// Open a connection and initialize the retrieval with a query.
status = startRetrieval(monitor);
if (!status.isOK())
{
return status;
}
if (endTime != startTime && System.currentTimeMillis() > endTime)
{
return Status.OK_STATUS;
}
retrievedRows = new ArrayList<Object[]>();
boolean hasNext = (lastBatchCount > 0);
while (hasNext)
{
if (monitor.isCanceled())
{
cancelRetrieval();
return Status.CANCEL_STATUS;
}
Object[] row = new Object[expressions.length];
retrievedRows.add(row);
for (int j = 0; j < expressions.length; j++)
{
row[j] = currentBatchValues.get(currentBatchIndex)[j];
}
monitor.worked(1);
if (endTime != startTime && System.currentTimeMillis() > endTime)
{
return Status.OK_STATUS;
}
hasNext = (retrievedRows.size() < maxRecords)
&& moveToNextRow(monitor);
}
if (endTime != startTime && System.currentTimeMillis() > endTime)
{
return Status.OK_STATUS;
}
return status;
}
private IStatus startRetrieval(IProgressMonitor monitor)
{
Hashtable<String, String> params = new Hashtable<String, String>();
params.put("entityType", entityType); //$NON-NLS-1$
params.put("expressions", expressionParameterString); //$NON-NLS-1$
params.put("username", username); //$NON-NLS-1$
params.put("password", password); //$NON-NLS-1$
params.put("timeout", Integer.toString(timeout)); //$NON-NLS-1$
try
{
StringWriter stringWriter = new StringWriter();
BufferedWriter writer = new BufferedWriter(stringWriter);
DesignerActivator.getDefault().getPreviewQueryManager().writeQuery(
dataSetUuid, writer);
writer.flush();
params.put("query", stringWriter.toString()); //$NON-NLS-1$
}
catch (IOException e)
{
e.printStackTrace();
}
WOActionDispatcher dispatcher = new WOActionDispatcher(serverUrl);
WOActionResponse response = dispatcher.send(
"designerPreview/startRetrieval", null, params); //$NON-NLS-1$
try
{
if (response.status().isOK())
{
BufferedReader reader = new BufferedReader(
new InputStreamReader(response.stream()));
previewSessionId = reader.readLine();
if (previewSessionId.equals("!!! ERROR")) //$NON-NLS-1$
{
return getStatusFromReader(reader);
}
}
}
catch (IOException e)
{
return new Status(IStatus.ERROR, DesignerActivator.PLUGIN_ID, e
.getMessage(), e);
}
finally
{
response.close();
}
IStatus status = cancelableGetNextBatch(monitor);
if (!status.isOK())
{
return status;
}
return Status.OK_STATUS;
}
private IStatus getStatusFromReader(BufferedReader reader)
{
StringBuilder message = new StringBuilder();
String line;
try
{
while ((line = reader.readLine()) != null)
message.append(line);
}
catch (IOException e)
{
message
.append(Messages.PREVIEW_JOB_BAD_RESPONSE);
}
return new Status(IStatus.ERROR, DesignerActivator.PLUGIN_ID, message
.toString());
}
private IStatus cancelRetrieval()
{
WOActionDispatcher dispatcher = new WOActionDispatcher(serverUrl);
WOActionResponse response = dispatcher.send(
"designerPreview/cancelRetrieval", previewSessionId, null); //$NON-NLS-1$
response.close();
return Status.CANCEL_STATUS;
}
private IStatus cancelableGetNextBatch(IProgressMonitor monitor)
{
IStatus status = Status.OK_STATUS;
synchronized (currentBatchLock)
{
currentBatchRetrieved = false;
}
Thread thread = new Thread()
{
@Override
public void run()
{
getNextBatch();
}
};
thread.start();
boolean alreadyCanceled = false;
// TODO needs to be fixed -- result set cancellation NOT the same as
// job cancellation
while (!isCurrentBatchRetrieved())
{
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
// Ignore exception.
}
if (!alreadyCanceled && monitor.isCanceled())
{
status = cancelRetrieval();
}
}
if (!alreadyCanceled)
status = currentBatchRetrievalStatus;
return status;
}
private boolean isCurrentBatchRetrieved()
{
synchronized (currentBatchLock)
{
return currentBatchRetrieved;
}
}
private void getNextBatch()
{
WOActionDispatcher dispatcher = new WOActionDispatcher(serverUrl);
WOActionResponse response = dispatcher.send(
"designerPreview/retrieveNextBatch", previewSessionId, null); //$NON-NLS-1$
lastBatchCount = 0;
currentBatchIndex = 0;
currentBatchValues = new ArrayList<Object[]>();
try
{
if (response.status().isOK())
{
ByteArrayOutputStream bufferStream = new ByteArrayOutputStream();
pipeStreams(response.stream(), bufferStream);
String bufferString = bufferStream.toString();
BufferedReader reader = new BufferedReader(new StringReader(
bufferString));
String line = reader.readLine();
if (line.equals("!!! ERROR")) //$NON-NLS-1$
{
synchronized (currentBatchLock)
{
currentBatchRetrieved = true;
currentBatchRetrievalStatus = getStatusFromReader(reader);
return;
}
}
ObjectInputStream input = new ObjectInputStream(
new ByteArrayInputStream(bufferStream.toByteArray()));
boolean hasNext = input.readBoolean();
while (hasNext)
{
Object[] currentRow = new Object[expressions.length];
for (int j = 0; j < expressions.length; j++)
currentRow[j] = input.readObject();
currentBatchValues.add(currentRow);
hasNext = input.readBoolean();
}
lastBatchCount = currentBatchValues.size();
input.close();
}
}
catch (Exception e)
{
synchronized (currentBatchLock)
{
currentBatchRetrieved = true;
currentBatchRetrievalStatus = new Status(IStatus.ERROR,
DesignerActivator.PLUGIN_ID, e.getMessage(), e);
return;
}
}
finally
{
response.close();
}
synchronized (currentBatchLock)
{
currentBatchRetrieved = true;
currentBatchRetrievalStatus = response.status();
}
}
private void pipeStreams(InputStream inputStream, OutputStream outputStream)
throws IOException
{
byte[] buffer = new byte[65536];
int bytesRead = inputStream.read(buffer, 0, 65536);
while (bytesRead > 0)
{
outputStream.write(buffer, 0, bytesRead);
bytesRead = inputStream.read(buffer, 0, 65536);
}
}
private boolean moveToNextRow(IProgressMonitor monitor)
{
if (lastBatchCount == 0)
return false;
currentBatchIndex++;
currentRow++;
if (currentBatchIndex == currentBatchValues.size())
{
IStatus status = cancelableGetNextBatch(monitor);
if (!status.isOK() || lastBatchCount == 0)
return false;
}
return true;
}
// ==== Fields ============================================================
private String serverUrl;
private String username;
private String password;
private int maxRecords;
private int timeout;
private ArrayList<Object[]> retrievedRows;
private int currentRow;
private int lastBatchCount;
private String dataSetUuid;
private String entityType;
private String[] expressions;
private String expressionParameterString;
private String previewSessionId;
private ArrayList<Object[]> currentBatchValues;
private int currentBatchIndex;
private boolean currentBatchRetrieved;
private Object currentBatchLock;
private IStatus currentBatchRetrievalStatus;
}