You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@manifoldcf.apache.org by kw...@apache.org on 2014/12/16 00:44:17 UTC

svn commit: r1645792 - in /manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler: interfaces/ jobs/ notification/

Author: kwright
Date: Mon Dec 15 23:44:17 2014
New Revision: 1645792

URL: http://svn.apache.org/r1645792
Log:
Add notification connection management

Added:
    manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notification/
    manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notification/NotificationConnection.java   (with props)
    manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notification/NotificationConnectionManager.java   (with props)
Modified:
    manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/CacheKeyFactory.java
    manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobManager.java
    manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobManager.java
    manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/Jobs.java

Modified: manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/CacheKeyFactory.java
URL: http://svn.apache.org/viewvc/manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/CacheKeyFactory.java?rev=1645792&r1=1645791&r2=1645792&view=diff
==============================================================================
--- manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/CacheKeyFactory.java (original)
+++ manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/CacheKeyFactory.java Mon Dec 15 23:44:17 2014
@@ -63,6 +63,24 @@ public class CacheKeyFactory extends org
     return "REPOSITORYCONNECTION_"+connectionName;
   }
 
+  /** Construct a key which represents the general list of notification connectors.
+  *@return the cache key.
+  */
+  public static String makeNotificationConnectionsKey()
+  {
+    return "NOTIFICATIONCONNECTIONS";
+  }
+
+  /** Construct a key which represents an individual notification connection.
+  *@param connectionName is the name of the connector.
+  *@return the cache key.
+  */
+  public static String makeNotificationConnectionKey(String connectionName)
+  {
+    return "NOTIFICATIONCONNECTION_"+connectionName;
+  }
+
+
   /** Construct a key which represents the general list of jobs - for queries
   * that depend on the fixed kind of job data, not the dynamic data (e.g. status)
   *@return the cache key.

Modified: manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobManager.java
URL: http://svn.apache.org/viewvc/manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobManager.java?rev=1645792&r1=1645791&r2=1645792&view=diff
==============================================================================
--- manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobManager.java (original)
+++ manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobManager.java Mon Dec 15 23:44:17 2014
@@ -118,6 +118,13 @@ public interface IJobManager
   public boolean checkIfReference(String connectionName)
     throws ManifoldCFException;
 
+  /** See if there's a reference to a notification connection name.
+  *@param connectionName is the name of the connection.
+  *@return true if there is a reference, false otherwise.
+  */
+  public boolean checkIfNotificationReference(String connectionName)
+    throws ManifoldCFException;
+
   /** See if there's a reference to an output connection name.
   *@param connectionName is the name of the connection.
   *@return true if there is a reference, false otherwise.
@@ -972,6 +979,13 @@ public interface IJobManager
   */
   public void noteConnectionChange(String connectionName)
     throws ManifoldCFException;
+
+  /** Note a change in notification connection configuration.
+  * This method will be called whenever a connection's configuration is modified, or when an external repository change
+  * is signalled.
+  */
+  public void noteNotificationConnectionChange(String connectionName)
+    throws ManifoldCFException;
     
   /**  Note the deregistration of an output connector used by the specified connections.
   * This method will be called when the connector is deregistered.  Jobs that use these connections

Modified: manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobManager.java
URL: http://svn.apache.org/viewvc/manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobManager.java?rev=1645792&r1=1645791&r2=1645792&view=diff
==============================================================================
--- manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobManager.java (original)
+++ manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobManager.java Mon Dec 15 23:44:17 2014
@@ -635,6 +635,17 @@ public class JobManager implements IJobM
     jobs.noteConnectionChange(connectionName);
   }
 
+  /** Note a change in notification connection configuration.
+  * This method will be called whenever a notification connection's configuration is modified, or when an external repository change
+  * is signalled.
+  */
+  @Override
+  public void noteNotificationConnectionChange(String connectionName)
+    throws ManifoldCFException
+  {
+    jobs.noteConnectionChange(connectionName);
+  }
+
   /** Note a change in output connection configuration.
   * This method will be called whenever a connection's configuration is modified, or when an external target config change
   * is signalled.
@@ -828,6 +839,17 @@ public class JobManager implements IJobM
     return jobs.checkIfReference(connectionName);
   }
 
+  /** See if there's a reference to a notification connection name.
+  *@param connectionName is the name of the connection.
+  *@return true if there is a reference, false otherwise.
+  */
+  @Override
+  public boolean checkIfNotificationReference(String connectionName)
+    throws ManifoldCFException
+  {
+    return jobs.checkIfNotificationReference(connectionName);
+  }
+
   /** See if there's a reference to an output connection name.
   *@param connectionName is the name of the connection.
   *@return true if there is a reference, false otherwise.

Modified: manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/Jobs.java
URL: http://svn.apache.org/viewvc/manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/Jobs.java?rev=1645792&r1=1645791&r2=1645792&view=diff
==============================================================================
--- manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/Jobs.java (original)
+++ manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/Jobs.java Mon Dec 15 23:44:17 2014
@@ -1542,6 +1542,16 @@ public class Jobs extends org.apache.man
     performUpdate(newValues,"WHERE "+query,list,null);
   }
 
+  /** Note a change in notification connection configuration.
+  * This method will be called whenever a notification connection's configuration is modified, or when an external repository change
+  * is signalled.
+  */
+  public void noteNotificationConnectionChange(String connectionName)
+    throws ManifoldCFException
+  {
+    // MHL
+  }
+
   /** Note a change in output connection configuration.
   * This method will be called whenever a connection's configuration is modified, or when an external target config change
   * is signalled.
@@ -2962,6 +2972,17 @@ public class Jobs extends org.apache.man
     return set.getRowCount() > 0;
   }
 
+  /** See if there's a reference to a notification connection name.
+  *@param connectionName is the name of the connection.
+  *@return true if there is a reference, false otherwise.
+  */
+  public boolean checkIfNotificationReference(String connectionName)
+    throws ManifoldCFException
+  {
+    // MHL
+    return false;
+  }
+
   /** See if there's a reference to an output connection name.
   *@param connectionName is the name of the connection.
   *@return true if there is a reference, false otherwise.

Added: manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notification/NotificationConnection.java
URL: http://svn.apache.org/viewvc/manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notification/NotificationConnection.java?rev=1645792&view=auto
==============================================================================
--- manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notification/NotificationConnection.java (added)
+++ manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notification/NotificationConnection.java Mon Dec 15 23:44:17 2014
@@ -0,0 +1,160 @@
+/* $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.notification;
+
+import org.apache.manifoldcf.core.interfaces.*;
+import org.apache.manifoldcf.crawler.interfaces.*;
+import java.util.*;
+
+/** An instance of this class represents a persistently-stored notification connection description.
+* This is the paper object meant for editing and manipulation.
+*/
+public class NotificationConnection implements INotificationConnection
+{
+  public static final String _rcsid = "@(#)$Id$";
+
+  // data
+  protected boolean isNew = true;
+  protected String name = null;
+  protected String description = null;
+  protected String className = null;
+  protected ConfigParams configParams = new ConfigParams();
+  protected int maxCount = 100;
+
+  /** Constructor.
+  */
+  public NotificationConnection()
+  {
+  }
+
+  /** Clone this object.
+  *@return the cloned object.
+  */
+  public NotificationConnection duplicate()
+  {
+    NotificationConnection rval = new NotificationConnection();
+    rval.isNew = isNew;
+    rval.name = name;
+    rval.description = description;
+    rval.className = className;
+    rval.maxCount = maxCount;
+    rval.configParams = configParams.duplicate();
+    return rval;
+  }
+
+  /** Set 'isnew' condition.
+  *@param isnew true if this is a new instance.
+  */
+  @Override
+  public void setIsNew(boolean isnew)
+  {
+    this.isNew = isnew;
+  }
+  
+  /** Get 'isnew' condition.
+  *@return true if this is a new connection, false otherwise.
+  */
+  @Override
+  public boolean getIsNew()
+  {
+    return isNew;
+  }
+
+  /** Set name.
+  *@param name is the name.
+  */
+  @Override
+  public void setName(String name)
+  {
+    this.name = name;
+  }
+
+  /** Get name.
+  *@return the name
+  */
+  @Override
+  public String getName()
+  {
+    return name;
+  }
+
+  /** Set description.
+  *@param description is the description.
+  */
+  @Override
+  public void setDescription(String description)
+  {
+    this.description = description;
+  }
+
+  /** Get description.
+  *@return the description
+  */
+  @Override
+  public String getDescription()
+  {
+    return description;
+  }
+
+  /** Set the class name.
+  *@param className is the class name.
+  */
+  @Override
+  public void setClassName(String className)
+  {
+    this.className = className;
+  }
+
+  /** Get the class name.
+  *@return the class name
+  */
+  @Override
+  public String getClassName()
+  {
+    return className;
+  }
+
+  /** Get the configuration parameters.
+  *@return the map.  Can be modified.
+  */
+  @Override
+  public ConfigParams getConfigParams()
+  {
+    return configParams;
+  }
+
+  /** Set the maximum size of the connection pool.
+  *@param maxCount is the maximum connection count per JVM.
+  */
+  @Override
+  public void setMaxConnections(int maxCount)
+  {
+    this.maxCount = maxCount;
+  }
+
+  /** Get the maximum size of the connection pool.
+  *@return the maximum size.
+  */
+  @Override
+  public int getMaxConnections()
+  {
+    return maxCount;
+  }
+
+}

Propchange: manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notification/NotificationConnection.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notification/NotificationConnection.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notification/NotificationConnectionManager.java
URL: http://svn.apache.org/viewvc/manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notification/NotificationConnectionManager.java?rev=1645792&view=auto
==============================================================================
--- manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notification/NotificationConnectionManager.java (added)
+++ manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notification/NotificationConnectionManager.java Mon Dec 15 23:44:17 2014
@@ -0,0 +1,842 @@
+/* $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.notification;
+
+import java.util.*;
+import org.apache.manifoldcf.core.interfaces.*;
+import org.apache.manifoldcf.crawler.interfaces.*;
+import org.apache.manifoldcf.authorities.interfaces.*;
+import org.apache.manifoldcf.crawler.interfaces.CacheKeyFactory;
+import org.apache.manifoldcf.crawler.system.ManifoldCF;
+
+
+/** This class is the manager of the notification connection description.  Inside, multiple database tables are managed,
+* with appropriate caching.
+* Note well: The database handle is instantiated here using the DBInterfaceFactory.  This is acceptable because the
+* actual database that this table is located in is fixed.
+* 
+* <br><br>
+* <b>notificationconnections</b>
+* <table border="1" cellpadding="3" cellspacing="0">
+* <tr class="TableHeadingColor">
+* <th>Field</th><th>Type</th><th>Description&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th>
+* <tr><td>connectionname</td><td>VARCHAR(32)</td><td>Primary Key</td></tr>
+* <tr><td>description</td><td>VARCHAR(255)</td><td></td></tr>
+* <tr><td>classname</td><td>VARCHAR(255)</td><td></td></tr>
+* <tr><td>maxcount</td><td>BIGINT</td><td></td></tr>
+* <tr><td>configxml</td><td>LONGTEXT</td><td></td></tr>
+* </table>
+* <br><br>
+*
+*/
+public class NotificationConnectionManager extends org.apache.manifoldcf.core.database.BaseTable implements INotificationConnectionManager
+{
+  public static final String _rcsid = "@(#)$Id$";
+
+  // Special field suffix
+  private final static String passwordSuffix = "password";
+
+  // Database fields
+  protected final static String nameField = "connectionname";     // Changed this to work around constraint bug in postgresql
+  protected final static String descriptionField = "description";
+  protected final static String classNameField = "classname";
+  protected final static String maxCountField = "maxcount";
+  protected final static String configField = "configxml";
+
+  protected static Random random = new Random();
+
+  // Cache manager
+  protected final ICacheManager cacheManager;
+  // Thread context
+  protected final IThreadContext threadContext;
+  // Lock manager
+  protected final ILockManager lockManager;
+  
+  protected final String notificationsLock = "NOTIFICATIONS_LOCK";
+  
+  /** Constructor.
+  *@param threadContext is the thread context.
+  */
+  public NotificationConnectionManager(IThreadContext threadContext, IDBInterface database)
+    throws ManifoldCFException
+  {
+    super(database,"notificationconnections");
+
+    cacheManager = CacheManagerFactory.make(threadContext);
+    lockManager = LockManagerFactory.make(threadContext);
+    this.threadContext = threadContext;
+  }
+
+  /** Install the manager.
+  */
+  @Override
+  public void install()
+    throws ManifoldCFException
+  {
+
+    // Always use a loop, and no transaction, as we may need to retry due to upgrade
+    while (true)
+    {
+      Map existing = getTableSchema(null,null);
+      if (existing == null)
+      {
+        // Install the "objects" table.
+        HashMap map = new HashMap();
+        map.put(nameField,new ColumnDescription("VARCHAR(32)",true,false,null,null,false));
+        map.put(descriptionField,new ColumnDescription("VARCHAR(255)",false,true,null,null,false));
+        map.put(classNameField,new ColumnDescription("VARCHAR(255)",false,false,null,null,false));
+        map.put(maxCountField,new ColumnDescription("BIGINT",false,false,null,null,false));
+        map.put(configField,new ColumnDescription("LONGTEXT",false,true,null,null,false));
+        performCreate(map,null);
+      }
+      else
+      {
+        // Upgrade code
+      }
+
+      // Index management
+      IndexDescription classIndex = new IndexDescription(false,new String[]{classNameField});
+      
+      // 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 (classIndex != null && id.equals(classIndex))
+          classIndex = 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 (classIndex != null)
+        performAddIndex(null,classIndex);
+      
+      break;
+    }
+  }
+
+  /** Uninstall the manager.
+  */
+  @Override
+  public void deinstall()
+    throws ManifoldCFException
+  {
+    beginTransaction();
+    try
+    {
+      performDrop(null);
+    }
+    catch (ManifoldCFException e)
+    {
+      signalRollback();
+      throw e;
+    }
+    catch (Error e)
+    {
+      signalRollback();
+      throw e;
+    }
+    finally
+    {
+      endTransaction();
+    }
+  }
+
+  /** Export configuration */
+  @Override
+  public void exportConfiguration(java.io.OutputStream os)
+    throws java.io.IOException, ManifoldCFException
+  {
+    // Write a version indicator
+    ManifoldCF.writeDword(os,1);
+    // Get the authority list
+    INotificationConnection[] list = getAllConnections();
+    // Write the number of authorities
+    ManifoldCF.writeDword(os,list.length);
+    // Loop through the list and write the individual repository connection info
+    int i = 0;
+    while (i < list.length)
+    {
+      INotificationConnection conn = list[i++];
+      ManifoldCF.writeString(os,conn.getName());
+      ManifoldCF.writeString(os,conn.getDescription());
+      ManifoldCF.writeString(os,conn.getClassName());
+      ManifoldCF.writeString(os,conn.getConfigParams().toXML());
+      ManifoldCF.writeDword(os,conn.getMaxConnections());
+    }
+  }
+
+  /** Import configuration */
+  @Override
+  public void importConfiguration(java.io.InputStream is)
+    throws java.io.IOException, ManifoldCFException
+  {
+    int version = ManifoldCF.readDword(is);
+    if (version != 1)
+      throw new java.io.IOException("Unknown notification connection configuration version: "+Integer.toString(version));
+    int count = ManifoldCF.readDword(is);
+    int i = 0;
+    while (i < count)
+    {
+      INotificationConnection conn = create();
+      conn.setName(ManifoldCF.readString(is));
+      conn.setDescription(ManifoldCF.readString(is));
+      conn.setClassName(ManifoldCF.readString(is));
+      conn.getConfigParams().fromXML(ManifoldCF.readString(is));
+      conn.setMaxConnections(ManifoldCF.readDword(is));
+      // Attempt to save this connection
+      save(conn);
+      i++;
+    }
+  }
+
+  /** Obtain a list of the notification connections, ordered by name.
+  *@return an array of connection objects.
+  */
+  @Override
+  public INotificationConnection[] getAllConnections()
+    throws ManifoldCFException
+  {
+    lockManager.enterReadLock(notificationsLock);
+    try
+    {
+      // Read all the tools
+      StringSetBuffer ssb = new StringSetBuffer();
+      ssb.add(getNotificationConnectionsKey());
+      StringSet localCacheKeys = new StringSet(ssb);
+      IResultSet set = performQuery("SELECT "+nameField+",lower("+nameField+") AS sortfield FROM "+getTableName()+" ORDER BY sortfield ASC",null,
+        localCacheKeys,null);
+      String[] names = new String[set.getRowCount()];
+      for (int i = 0; i < names.length; i++)
+      {
+        IResultRow row = set.getRow(i);
+        names[i] = row.getValue(nameField).toString();
+      }
+      return loadMultiple(names);
+    }
+    finally
+    {
+      lockManager.leaveReadLock(notificationsLock);
+    }
+  }
+
+  /** Load a notification connection by name.
+  *@param name is the name of the notification connection.
+  *@return the loaded connection object, or null if not found.
+  */
+  @Override
+  public INotificationConnection load(String name)
+    throws ManifoldCFException
+  {
+    return loadMultiple(new String[]{name})[0];
+  }
+
+  protected final static int FETCH_MAX = 200;
+  
+  /** Load multiple notification connections by name.
+  *@param names are the names to load.
+  *@return the loaded connection objects.
+  */
+  @Override
+  public INotificationConnection[] loadMultiple(String[] names)
+    throws ManifoldCFException
+  {
+    INotificationConnection[] rval = new INotificationConnection[names.length];
+    if (names.length == 0)
+      return rval;
+    int inputIndex = 0;
+    int outputIndex = 0;
+    while (names.length - inputIndex > FETCH_MAX)
+    {
+      outputIndex = loadMultipleInternal(rval,outputIndex,names,inputIndex,FETCH_MAX);
+      inputIndex += FETCH_MAX;
+    }
+    loadMultipleInternal(rval,outputIndex,names,inputIndex,names.length-inputIndex);
+    return rval;
+  }
+  
+  protected int loadMultipleInternal(INotificationConnection[] rval, int outputIndex, String[] fetchNames, int inputIndex, int length)
+    throws ManifoldCFException
+  {
+    // Build description objects
+    NotificationConnectionDescription[] objectDescriptions = new NotificationConnectionDescription[length];
+    StringSetBuffer ssb = new StringSetBuffer();
+    for (int i = 0; i < length; i++)
+    {
+      ssb.clear();
+      String name = fetchNames[inputIndex + i];
+      ssb.add(getNotificationConnectionKey(name));
+      objectDescriptions[i] = new NotificationConnectionDescription(name,new StringSet(ssb));
+    }
+
+    NotificationConnectionExecutor exec = new NotificationConnectionExecutor(this,objectDescriptions);
+    cacheManager.findObjectsAndExecute(objectDescriptions,null,exec,getTransactionID());
+    INotificationConnection[] results = exec.getResults();
+    for (INotificationConnection result : results)
+    {
+      rval[outputIndex++] = result;
+    }
+    return outputIndex;
+  }
+
+  /** Create a new notification connection object.
+  *@return the new object.
+  */
+  @Override
+  public INotificationConnection create()
+    throws ManifoldCFException
+  {
+    NotificationConnection rval = new NotificationConnection();
+    return rval;
+  }
+
+  /** Save a notification connection object.
+  *@param object is the object to save.
+  *@return true if the object was created, false otherwise.
+  */
+  @Override
+  public boolean save(INotificationConnection object)
+    throws ManifoldCFException
+  {
+    StringSetBuffer ssb = new StringSetBuffer();
+    ssb.add(getNotificationConnectionsKey());
+    ssb.add(getNotificationConnectionKey(object.getName()));
+    StringSet cacheKeys = new StringSet(ssb);
+    while (true)
+    {
+      // Catch deadlock condition
+      long sleepAmt = 0L;
+      try
+      {
+        lockManager.enterNonExWriteLock(notificationsLock);
+        try
+        {
+          ICacheHandle ch = cacheManager.enterCache(null,cacheKeys,getTransactionID());
+          try
+          {
+            beginTransaction();
+            try
+            {
+              //performLock();
+              // Notify of a change to the configuration
+              ManifoldCF.noteConfigurationChange();
+              boolean isNew = object.getIsNew();
+              // See whether the instance exists
+              ArrayList params = new ArrayList();
+              String query = buildConjunctionClause(params,new ClauseDescription[]{
+                new UnitaryClause(nameField,object.getName())});
+              IResultSet set = performQuery("SELECT * FROM "+getTableName()+" WHERE "+
+                query+" FOR UPDATE",params,null,null);
+              HashMap values = new HashMap();
+              values.put(descriptionField,object.getDescription());
+              values.put(classNameField,object.getClassName());
+              values.put(maxCountField,new Long((long)object.getMaxConnections()));
+              String configXML = object.getConfigParams().toXML();
+              values.put(configField,configXML);
+              boolean notificationNeeded = false;
+              boolean isCreated;
+              
+              if (set.getRowCount() > 0)
+              {
+                // If the object is supposedly new, it is bad that we found one that already exists.
+                if (isNew)
+                  throw new ManifoldCFException("Notification connection '"+object.getName()+"' already exists");
+                isCreated = false;
+                IResultRow row = set.getRow(0);
+                String oldXML = (String)row.getValue(configField);
+                if (oldXML == null || !oldXML.equals(configXML))
+                  notificationNeeded = true;
+                
+                // Update
+                params.clear();
+                query = buildConjunctionClause(params,new ClauseDescription[]{
+                  new UnitaryClause(nameField,object.getName())});
+                performUpdate(values," WHERE "+query,params,null);
+              }
+              else
+              {
+                // If the object is not supposed to be new, it is bad that we did not find one.
+                if (!isNew)
+                  throw new ManifoldCFException("Notification connection '"+object.getName()+"' no longer exists");
+                isCreated = true;
+                // Insert
+                values.put(nameField,object.getName());
+                // We only need the general key because this is new.
+                performInsert(values,null);
+              }
+
+              // If notification required, do it.
+              if (notificationNeeded)
+              {
+                IJobManager jobManager = JobManagerFactory.make(threadContext);
+                jobManager.noteNotificationConnectionChange(object.getName());
+              }
+
+              cacheManager.invalidateKeys(ch);
+              return isCreated;
+            }
+            catch (ManifoldCFException e)
+            {
+              signalRollback();
+              throw e;
+            }
+            catch (Error e)
+            {
+              signalRollback();
+              throw e;
+            }
+            finally
+            {
+              endTransaction();
+            }
+          }
+          finally
+          {
+            cacheManager.leaveCache(ch);
+          }
+        }
+        finally
+        {
+          lockManager.leaveNonExWriteLock(notificationsLock);
+        }
+      }
+      catch (ManifoldCFException e)
+      {
+        // Is this a deadlock exception?  If so, we want to try again.
+        if (e.getErrorCode() != ManifoldCFException.DATABASE_TRANSACTION_ABORT)
+          throw e;
+        sleepAmt = getSleepAmt();
+      }
+      finally
+      {
+        sleepFor(sleepAmt);
+      }
+    }
+  }
+
+  /** Delete a notification connection.
+  *@param name is the name of the connection to delete.  If the
+  * name does not exist, no error is returned.
+  */
+  @Override
+  public void delete(String name)
+    throws ManifoldCFException
+  {
+    // Grab a job manager handle.  We will need to check if any jobs refer to this connection.
+    IJobManager jobManager = JobManagerFactory.make(threadContext);
+    StringSetBuffer ssb = new StringSetBuffer();
+    ssb.add(getNotificationConnectionsKey());
+    ssb.add(getNotificationConnectionKey(name));
+    StringSet cacheKeys = new StringSet(ssb);
+    lockManager.enterNonExWriteLock(notificationsLock);
+    try
+    {
+      ICacheHandle ch = cacheManager.enterCache(null,cacheKeys,getTransactionID());
+      try
+      {
+        beginTransaction();
+        try
+        {
+          // Check if any jobs refer to this connection name
+          if (jobManager.checkIfNotificationReference(name))
+            throw new ManifoldCFException("Can't delete notification connection '"+name+"': existing jobs refer to it");
+          ManifoldCF.noteConfigurationChange();
+          ArrayList params = new ArrayList();
+          String query = buildConjunctionClause(params,new ClauseDescription[]{
+            new UnitaryClause(nameField,name)});
+          performDelete("WHERE "+query,params,null);
+          cacheManager.invalidateKeys(ch);
+        }
+        catch (ManifoldCFException e)
+        {
+          signalRollback();
+          throw e;
+        }
+        catch (Error e)
+        {
+          signalRollback();
+          throw e;
+        }
+        finally
+        {
+          endTransaction();
+        }
+      }
+      finally
+      {
+        cacheManager.leaveCache(ch);
+      }
+    }
+    finally
+    {
+      lockManager.leaveNonExWriteLock(notificationsLock);
+    }
+  }
+
+  /** Get a list of notification connections that share the same connector.
+  *@param className is the class name of the connector.
+  *@return the notification connections that use that connector.
+  */
+  @Override
+  public String[] findConnectionsForConnector(String className)
+    throws ManifoldCFException
+  {
+    StringSetBuffer ssb = new StringSetBuffer();
+    ssb.add(getNotificationConnectionsKey());
+    StringSet localCacheKeys = new StringSet(ssb);
+    ArrayList params = new ArrayList();
+    String query = buildConjunctionClause(params,new ClauseDescription[]{
+      new UnitaryClause(classNameField,className)});
+    IResultSet set = performQuery("SELECT "+nameField+" FROM "+getTableName()+" WHERE "+query,params,
+      localCacheKeys,null);
+    String[] rval = new String[set.getRowCount()];
+    int i = 0;
+    while (i < rval.length)
+    {
+      IResultRow row = set.getRow(i);
+      rval[i] = (String)row.getValue(nameField);
+      i++;
+    }
+    java.util.Arrays.sort(rval);
+    return rval;
+  }
+
+  /** Check if underlying connector exists.
+  *@param name is the name of the connection to check.
+  *@return true if the underlying connector is registered.
+  */
+  @Override
+  public boolean checkConnectorExists(String name)
+    throws ManifoldCFException
+  {
+    beginTransaction();
+    try
+    {
+      StringSetBuffer ssb = new StringSetBuffer();
+      ssb.add(getNotificationConnectionKey(name));
+      StringSet localCacheKeys = new StringSet(ssb);
+      ArrayList params = new ArrayList();
+      String query = buildConjunctionClause(params,new ClauseDescription[]{
+        new UnitaryClause(nameField,name)});
+      IResultSet set = performQuery("SELECT "+classNameField+" FROM "+getTableName()+" WHERE "+query,params,
+        localCacheKeys,null);
+      if (set.getRowCount() == 0)
+        throw new ManifoldCFException("No such connection: '"+name+"'");
+      IResultRow row = set.getRow(0);
+      String className = (String)row.getValue(classNameField);
+      INotificationConnectorManager cm = NotificationConnectorManagerFactory.make(threadContext);
+      return cm.isInstalled(className);
+    }
+    catch (ManifoldCFException e)
+    {
+      signalRollback();
+      throw e;
+    }
+    catch (Error e)
+    {
+      signalRollback();
+      throw e;
+    }
+    finally
+    {
+      endTransaction();
+    }
+  }
+
+  // Schema related
+  /** Return the name column.
+  *@return the name column.
+  */
+  @Override
+  public String getConnectionNameColumn()
+  {
+    return nameField;
+  }
+
+
+  // Caching strategy: Individual connection descriptions are cached, and there is a global cache key for the list of
+  // repository connections.
+
+  /** Construct a key which represents the general list of notification connectors.
+  *@return the cache key.
+  */
+  protected static String getNotificationConnectionsKey()
+  {
+    return CacheKeyFactory.makeNotificationConnectionsKey();
+  }
+
+  /** Construct a key which represents an individual notification connection.
+  *@param connectionName is the name of the connector.
+  *@return the cache key.
+  */
+  protected static String getNotificationConnectionKey(String connectionName)
+  {
+    return CacheKeyFactory.makeNotificationConnectionKey(connectionName);
+  }
+
+  // Other utility methods.
+
+  /** Fetch multiple notification connections at a single time.
+  *@param connectionNames are a list of connection names.
+  *@return the corresponding repository connection objects.
+  */
+  protected NotificationConnection[] getNotificationConnectionsMultiple(String[] connectionNames)
+    throws ManifoldCFException
+  {
+    NotificationConnection[] rval = new NotificationConnection[connectionNames.length];
+    HashMap returnIndex = new HashMap();
+    int i = 0;
+    while (i < connectionNames.length)
+    {
+      rval[i] = null;
+      returnIndex.put(connectionNames[i],new Integer(i));
+      i++;
+    }
+    beginTransaction();
+    try
+    {
+      i = 0;
+      ArrayList params = new ArrayList();
+      int j = 0;
+      int maxIn = maxClauseGetNotificationConnectionsChunk();
+      while (i < connectionNames.length)
+      {
+        if (j == maxIn)
+        {
+          getNotificationConnectionsChunk(rval,returnIndex,params);
+          params.clear();
+          j = 0;
+        }
+        params.add(connectionNames[i]);
+        i++;
+        j++;
+      }
+      if (j > 0)
+        getNotificationConnectionsChunk(rval,returnIndex,params);
+      return rval;
+    }
+    catch (Error e)
+    {
+      signalRollback();
+      throw e;
+    }
+    catch (ManifoldCFException e)
+    {
+      signalRollback();
+      throw e;
+    }
+    finally
+    {
+      endTransaction();
+    }
+  }
+
+  /** Calculate how many notification connections to get at once.
+  */
+  protected int maxClauseGetNotificationConnectionsChunk()
+  {
+    return findConjunctionClauseMax(new ClauseDescription[]{});
+  }
+  
+  /** Read a chunk of notification connections.
+  *@param rval is the place to put the read policies.
+  *@param returnIndex is a map from the object id (resource id) and the rval index.
+  *@param params is the set of parameters.
+  */
+  protected void getNotificationConnectionsChunk(NotificationConnection[] rval, Map returnIndex, ArrayList params)
+    throws ManifoldCFException
+  {
+    ArrayList list = new ArrayList();
+    String query = buildConjunctionClause(list,new ClauseDescription[]{
+      new MultiClause(nameField,params)});
+    IResultSet set = performQuery("SELECT * FROM "+getTableName()+" WHERE "+
+      query,list,null,null);
+    int i = 0;
+    while (i < set.getRowCount())
+    {
+      IResultRow row = set.getRow(i++);
+      String name = row.getValue(nameField).toString();
+      int index = ((Integer)returnIndex.get(name)).intValue();
+      NotificationConnection rc = new NotificationConnection();
+      rc.setIsNew(false);
+      rc.setName(name);
+      rc.setDescription((String)row.getValue(descriptionField));
+      rc.setClassName((String)row.getValue(classNameField));
+      rc.setMaxConnections((int)((Long)row.getValue(maxCountField)).longValue());
+      String xml = (String)row.getValue(configField);
+      if (xml != null && xml.length() > 0)
+        rc.getConfigParams().fromXML(xml);
+      rval[index] = rc;
+    }
+
+  }
+
+  // The cached instance will be a NotificationConnection.  The cached version will be duplicated when it is returned
+  // from the cache.
+  //
+  // The description object is based completely on the name.
+
+  /** This is the object description for a repository connection object.
+  */
+  protected static class NotificationConnectionDescription extends org.apache.manifoldcf.core.cachemanager.BaseDescription
+  {
+    protected String connectionName;
+    protected String criticalSectionName;
+    protected StringSet cacheKeys;
+
+    public NotificationConnectionDescription(String connectionName, StringSet invKeys)
+    {
+      super("notificationconnectioncache");
+      this.connectionName = connectionName;
+      criticalSectionName = getClass().getName()+"-"+connectionName;
+      cacheKeys = invKeys;
+    }
+
+    public String getConnectionName()
+    {
+      return connectionName;
+    }
+
+    public int hashCode()
+    {
+      return connectionName.hashCode();
+    }
+
+    public boolean equals(Object o)
+    {
+      if (!(o instanceof NotificationConnectionDescription))
+        return false;
+      NotificationConnectionDescription d = (NotificationConnectionDescription)o;
+      return d.connectionName.equals(connectionName);
+    }
+
+    public String getCriticalSectionName()
+    {
+      return criticalSectionName;
+    }
+
+    /** Get the cache keys for an object (which may or may not exist yet in
+    * the cache).  This method is called in order for cache manager to throw the correct locks.
+    * @return the object's cache keys, or null if the object should not
+    * be cached.
+    */
+    public StringSet getObjectKeys()
+    {
+      return cacheKeys;
+    }
+
+  }
+
+  /** This is the executor object for locating notification connection objects.
+  */
+  protected static class NotificationConnectionExecutor extends org.apache.manifoldcf.core.cachemanager.ExecutorBase
+  {
+    // Member variables
+    protected NotificationConnectionManager thisManager;
+    protected NotificationConnection[] returnValues;
+    protected HashMap returnMap = new HashMap();
+
+    /** Constructor.
+    *@param manager is the NotificationManager.
+    *@param objectDescriptions are the object descriptions.
+    */
+    public NotificationConnectionExecutor(NotificationConnectionManager manager, NotificationConnectionDescription[] objectDescriptions)
+    {
+      super();
+      thisManager = manager;
+      returnValues = new NotificationConnection[objectDescriptions.length];
+      int i = 0;
+      while (i < objectDescriptions.length)
+      {
+        returnMap.put(objectDescriptions[i].getConnectionName(),new Integer(i));
+        i++;
+      }
+    }
+
+    /** Get the result.
+    *@return the looked-up or read cached instances.
+    */
+    public NotificationConnection[] getResults()
+    {
+      return returnValues;
+    }
+
+    /** Create a set of new objects to operate on and cache.  This method is called only
+    * if the specified object(s) are NOT available in the cache.  The specified objects
+    * should be created and returned; if they are not created, it means that the
+    * execution cannot proceed, and the execute() method will not be called.
+    * @param objectDescriptions is the set of unique identifier of the object.
+    * @return the newly created objects to cache, or null, if any object cannot be created.
+    *  The order of the returned objects must correspond to the order of the object descriptinos.
+    */
+    public Object[] create(ICacheDescription[] objectDescriptions) throws ManifoldCFException
+    {
+      // Turn the object descriptions into the parameters for the ToolInstance requests
+      String[] connectionNames = new String[objectDescriptions.length];
+      int i = 0;
+      while (i < connectionNames.length)
+      {
+        NotificationConnectionDescription desc = (NotificationConnectionDescription)objectDescriptions[i];
+        connectionNames[i] = desc.getConnectionName();
+        i++;
+      }
+
+      return thisManager.getNotificationConnectionsMultiple(connectionNames);
+    }
+
+
+    /** Notify the implementing class of the existence of a cached version of the
+    * object.  The object is passed to this method so that the execute() method below
+    * will have it available to operate on.  This method is also called for all objects
+    * that are freshly created as well.
+    * @param objectDescription is the unique identifier of the object.
+    * @param cachedObject is the cached object.
+    */
+    public void exists(ICacheDescription objectDescription, Object cachedObject) throws ManifoldCFException
+    {
+      // Cast what came in as what it really is
+      NotificationConnectionDescription objectDesc = (NotificationConnectionDescription)objectDescription;
+      NotificationConnection ci = (NotificationConnection)cachedObject;
+
+      // Duplicate it!
+      if (ci != null)
+        ci = ci.duplicate();
+
+      // In order to make the indexes line up, we need to use the hashtable built by
+      // the constructor.
+      returnValues[((Integer)returnMap.get(objectDesc.getConnectionName())).intValue()] = ci;
+    }
+
+    /** Perform the desired operation.  This method is called after either createGetObject()
+    * or exists() is called for every requested object.
+    */
+    public void execute() throws ManifoldCFException
+    {
+      // Does nothing; we only want to fetch objects in this cacher.
+    }
+
+
+  }
+
+}

Propchange: manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notification/NotificationConnectionManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notification/NotificationConnectionManager.java
------------------------------------------------------------------------------
    svn:keywords = Id