/* $Id$ */
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.manifoldcf.crawler.jobs;
import org.apache.manifoldcf.core.interfaces.*;
import org.apache.manifoldcf.agents.interfaces.*;
import org.apache.manifoldcf.crawler.interfaces.*;
import java.util.*;
/** This class manages the "pipelines" table, which contains the ordered transformation
* connections and their specification data.
*
* <br><br>
* <b>jobpipelines</b>
* <table border="1" cellpadding="3" cellspacing="0">
* <tr class="TableHeadingColor">
* <th>Field</th><th>Type</th><th>Description </th>
* <tr><td>ownerid</td><td>BIGINT</td><td>Reference:jobs.id</td></tr>
* <tr><td>ordinal</td><td>BIGINT</td><td></td></tr>
* <tr><td>prerequisite</td><td>BIGINT</td><td></td></tr>
* <tr><td>outputname</td><td>VARCHAR(32)</td><td></td></tr>
* <tr><td>transformationname</td><td>VARCHAR(32)</td><td></td></tr>
* <tr><td>connectiondesc</td><td>VARCHAR(255)</td><td></td></tr>
* <tr><td>connectionspec</td><td>LONGTEXT</td><td></td></tr>
* </table>
* <br><br>
*
*/
public class PipelineManager extends org.apache.manifoldcf.core.database.BaseTable
{
public static final String _rcsid = "@(#)$Id$";
// Schema
public final static String ownerIDField = "ownerid";
public final static String ordinalField = "ordinal";
public final static String prerequisiteField = "prerequisite";
public final static String outputNameField = "outputname";
public final static String transformationNameField = "transformationname";
public final static String connectionDescriptionField = "connectiondesc";
public final static String connectionSpecField = "connectionspec";
/** Constructor.
*@param threadContext is the thread context.
*@param database is the database instance.
*/
public PipelineManager(IThreadContext threadContext, IDBInterface database)
throws ManifoldCFException
{
super(database,"jobpipelines");
}
/** Install or upgrade.
*@param ownerTable is the name of the table that owns this one.
*@param owningTablePrimaryKey is the primary key of the owning table.
*/
public void install(String ownerTable, String owningTablePrimaryKey,
String outputTableName, String outputTableNameField,
String transformationTableName, String transformationTableNameField)
throws ManifoldCFException
{
// Standard practice: Outer loop to support upgrades
while (true)
{
Map existing = getTableSchema(null,null);
if (existing == null)
{
HashMap map = new HashMap();
map.put(ownerIDField,new ColumnDescription("BIGINT",false,false,ownerTable,owningTablePrimaryKey,false));
map.put(ordinalField,new ColumnDescription("BIGINT",false,false,null,null,false));
map.put(prerequisiteField,new ColumnDescription("BIGINT",false,true,null,null,false));
map.put(outputNameField,new ColumnDescription("VARCHAR(32)",false,true,outputTableName,outputTableNameField,false));
map.put(transformationNameField,new ColumnDescription("VARCHAR(32)",false,true,transformationTableName,transformationTableNameField,false));
map.put(connectionDescriptionField,new ColumnDescription("VARCHAR(255)",false,true,null,null,false));
map.put(connectionSpecField,new ColumnDescription("LONGTEXT",false,true,null,null,false));
performCreate(map,null);
}
else
{
// Upgrade code goes here, if needed.
}
// Index management
IndexDescription ownerIndex = new IndexDescription(false,new String[]{ownerIDField});
IndexDescription transformationNameIndex = new IndexDescription(false,new String[]{transformationNameField});
IndexDescription outputNameIndex = new IndexDescription(false,new String[]{outputNameField});
// Get rid of indexes that shouldn't be there
Map indexes = getTableIndexes(null,null);
Iterator iter = indexes.keySet().iterator();
while (iter.hasNext())
{
String indexName = (String)iter.next();
IndexDescription id = (IndexDescription)indexes.get(indexName);
if (ownerIndex != null && id.equals(ownerIndex))
ownerIndex = null;
else if (transformationNameIndex != null && id.equals(transformationNameIndex))
transformationNameIndex = null;
else if (outputNameIndex != null && id.equals(outputNameIndex))
outputNameIndex = null;
else if (indexName.indexOf("_pkey") == -1)
// This index shouldn't be here; drop it
performRemoveIndex(indexName);
}
// Add the ones we didn't find
if (ownerIndex != null)
performAddIndex(null,ownerIndex);
if (transformationNameIndex != null)
performAddIndex(null,transformationNameIndex);
if (outputNameIndex != null)
performAddIndex(null,outputNameIndex);
break;
}
}
/** Uninstall.
*/
public void deinstall()
throws ManifoldCFException
{
performDrop(null);
}
/** Build a query clause matching a set of transformation connection names.
*/
public void buildTransformationQueryClause(StringBuilder query, ArrayList params,
String parentIDField, List<String> connectionNames)
{
query.append("SELECT 'x' FROM ").append(getTableName()).append(" WHERE ");
query.append(buildConjunctionClause(params,new ClauseDescription[]{
new JoinClause(parentIDField,ownerIDField),
new MultiClause(transformationNameField,connectionNames)}));
}
/** Build a query clause matching a set of output connection names.
*/
public void buildOutputQueryClause(StringBuilder query, ArrayList params,
String parentIDField, List<String> connectionNames)
{
query.append("SELECT 'x' FROM ").append(getTableName()).append(" WHERE ");
query.append(buildConjunctionClause(params,new ClauseDescription[]{
new JoinClause(parentIDField,ownerIDField),
new MultiClause(outputNameField,connectionNames)}));
}
/** Get all the transformation connection names for a job.
*@param ownerID is the job ID.
*@return the set of connection names.
*/
public String[] getTransformationConnectionNames(Long ownerID)
throws ManifoldCFException
{
ArrayList newList = new ArrayList();
StringBuilder query = new StringBuilder("SELECT ");
query.append(transformationNameField).append(" FROM ").append(getTableName()).append(" WHERE ");
query.append(buildConjunctionClause(newList,new ClauseDescription[]{
new UnitaryClause(ownerIDField,ownerID),
new NullCheckClause(transformationNameField,false)}));
IResultSet set = performQuery(query.toString(),newList,null,null);
String[] rval = new String[set.getRowCount()];
for (int i = 0; i < set.getRowCount(); i++)
{
IResultRow row = set.getRow(i);
rval[i] = (String)row.getValue(transformationNameField);
}
return rval;
}
/** Get all the output connection names for a job.
*@param ownerID is the job ID.
*@return the set of connection names.
*/
public String[] getOutputConnectionNames(Long ownerID)
throws ManifoldCFException
{
ArrayList newList = new ArrayList();
StringBuilder query = new StringBuilder("SELECT ");
query.append(outputNameField).append(" FROM ").append(getTableName()).append(" WHERE ");
query.append(buildConjunctionClause(newList,new ClauseDescription[]{
new UnitaryClause(ownerIDField,ownerID),
new NullCheckClause(outputNameField,false)}));
IResultSet set = performQuery(query.toString(),newList,null,null);
String[] rval = new String[set.getRowCount()];
for (int i = 0; i < set.getRowCount(); i++)
{
IResultRow row = set.getRow(i);
rval[i] = (String)row.getValue(outputNameField);
}
return rval;
}
/** Fill in a set of pipelines corresponding to a set of owner id's.
*@param returnValues is a map keyed by ownerID, with value of JobDescription.
*@param ownerIDList is the list of owner id's.
*@param ownerIDParams is the corresponding set of owner id parameters.
*/
public void getRows(Map<Long,JobDescription> returnValues, String ownerIDList, ArrayList ownerIDParams)
throws ManifoldCFException
{
IResultSet set = performQuery("SELECT * FROM "+getTableName()+" WHERE "+ownerIDField+" IN ("+ownerIDList+") ORDER BY "+ordinalField+" ASC",ownerIDParams,
null,null);
for (int i = 0; i < set.getRowCount(); i++)
{
IResultRow row = set.getRow(i);
Long ownerID = (Long)row.getValue(ownerIDField);
Long prerequisite = (Long)row.getValue(prerequisiteField);
String outputName = (String)row.getValue(outputNameField);
String transformationName = (String)row.getValue(transformationNameField);
String transformationDesc = (String)row.getValue(connectionDescriptionField);
String transformationSpec = (String)row.getValue(connectionSpecField);
boolean isOutput = outputName != null && outputName.length() > 0;
int prerequisiteValue = (prerequisite == null)?-1:(int)prerequisite.longValue();
JobDescription jd = returnValues.get(ownerID);
jd.addPipelineStage(prerequisiteValue,isOutput,isOutput?outputName:transformationName,transformationDesc).fromXML(transformationSpec);
}
}
/** Compare rows in job description with what's currently in the database.
*@param ownerID is the owning identifier.
*@param job is a job description.
*/
public boolean compareRows(Long ownerID, IJobDescription job)
throws ManifoldCFException
{
// Compute a set of the outputs
Set<String> outputSet = new HashSet<String>();
for (int i = 0; i < job.countPipelineStages(); i++)
{
if (job.getPipelineStageIsOutputConnection(i))
{
String outputName = job.getPipelineStageConnectionName(i);
if (outputSet.contains(outputName))
throw new ManifoldCFException("Output name '"+outputName+"' is duplicated within job; not allowed");
outputSet.add(outputName);
}
}
ArrayList params = new ArrayList();
String query = buildConjunctionClause(params,new ClauseDescription[]{
new UnitaryClause(ownerIDField,ownerID)});
IResultSet set = performQuery("SELECT * FROM "+getTableName()+" WHERE "+
query+" ORDER BY "+ordinalField+" ASC",params,null,null);
if (set.getRowCount() != job.countPipelineStages())
return false;
for (int i = 0; i < set.getRowCount(); i++)
{
IResultRow row = set.getRow(i);
String outputConnectionName = (String)row.getValue(outputNameField);
boolean isOutputConnection = outputConnectionName != null && outputConnectionName.length() > 0;
if (isOutputConnection)
{
if (!outputSet.contains(outputConnectionName))
throw new ManifoldCFException("Output name '"+outputConnectionName+"' removed from job; not allowed");
}
String transformationConnectionName = (String)row.getValue(transformationNameField);
Long prerequisite = (Long)row.getValue(prerequisiteField);
String spec = (String)row.getValue(connectionSpecField);
if (spec == null)
spec = "";
int prerequisiteValue = (prerequisite==null)?-1:(int)prerequisite.longValue();
if (job.getPipelineStagePrerequisite(i) != prerequisiteValue)
return false;
if (job.getPipelineStageIsOutputConnection(i) != isOutputConnection)
return false;
if (!job.getPipelineStageConnectionName(i).equals(isOutputConnection?outputConnectionName:transformationConnectionName))
return false;
if (!job.getPipelineStageSpecification(i).toXML().equals(spec))
return false;
}
return true;
}
/** Write an output stage (part of the upgrade code).
*/
public void writeOutputStage(Long ownerID, String outputConnectionName, String outputSpecification)
throws ManifoldCFException
{
HashMap map = new HashMap();
map.put(ownerIDField,ownerID);
map.put(ordinalField,new Long(0));
map.put(outputNameField,outputConnectionName);
map.put(connectionSpecField,outputSpecification);
performInsert(map,null);
}
/** Write a pipeline list into the database.
*@param ownerID is the owning identifier.
*@param job is the job description that is the source of the pipeline.
*/
public void writeRows(Long ownerID, IJobDescription job)
throws ManifoldCFException
{
beginTransaction();
try
{
HashMap map = new HashMap();
for (int i = 0; i < job.countPipelineStages(); i++)
{
boolean isOutput = job.getPipelineStageIsOutputConnection(i);
int prerequisite = job.getPipelineStagePrerequisite(i);
String pipelineConnectionName = job.getPipelineStageConnectionName(i);
String pipelineStageDescription = job.getPipelineStageDescription(i);
Specification os = job.getPipelineStageSpecification(i);
map.clear();
map.put(ownerIDField,ownerID);
map.put(ordinalField,new Long((long)i));
if (prerequisite != -1)
map.put(prerequisiteField,new Long(prerequisite));
if (isOutput)
map.put(outputNameField,pipelineConnectionName);
else
map.put(transformationNameField,pipelineConnectionName);
if (pipelineStageDescription != null && pipelineStageDescription.length() > 0)
map.put(connectionDescriptionField,pipelineStageDescription);
map.put(connectionSpecField,os.toXML());
performInsert(map,null);
}
}
catch (ManifoldCFException e)
{
signalRollback();
throw e;
}
catch (Error e)
{
signalRollback();
throw e;
}
finally
{
endTransaction();
}
}
/** Delete rows.
*@param ownerID is the owner whose rows to delete.
*/
public void deleteRows(Long ownerID)
throws ManifoldCFException
{
ArrayList list = new ArrayList();
String query = buildConjunctionClause(list,new ClauseDescription[]{
new UnitaryClause(ownerIDField,ownerID)});
performDelete("WHERE "+query,list,null);
}
}