Author: kwright
Date: Tue Dec 23 14:46:43 2014
New Revision: 1647585

URL: http://svn.apache.org/r1647585
Log:
Implement CONNECTORS-1119.

Added:
    
manifoldcf/trunk/connectors/email/connector/src/main/java/org/apache/manifoldcf/crawler/notifications/
      - copied from r1647584, 
manifoldcf/branches/CONNECTORS-1119/connectors/email/connector/src/main/java/org/apache/manifoldcf/crawler/notifications/
    
manifoldcf/trunk/connectors/email/connector/src/main/native2ascii/org/apache/manifoldcf/crawler/notifications/
      - copied from r1647584, 
manifoldcf/branches/CONNECTORS-1119/connectors/email/connector/src/main/native2ascii/org/apache/manifoldcf/crawler/notifications/
    
manifoldcf/trunk/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/notifications/
      - copied from r1647584, 
manifoldcf/branches/CONNECTORS-1119/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/notifications/
    manifoldcf/trunk/framework/crawler-ui/src/main/webapp/editnotification.jsp
      - copied unchanged from r1647584, 
manifoldcf/branches/CONNECTORS-1119/framework/crawler-ui/src/main/webapp/editnotification.jsp
    manifoldcf/trunk/framework/crawler-ui/src/main/webapp/listnotifications.jsp
      - copied unchanged from r1647584, 
manifoldcf/branches/CONNECTORS-1119/framework/crawler-ui/src/main/webapp/listnotifications.jsp
    manifoldcf/trunk/framework/crawler-ui/src/main/webapp/viewnotification.jsp
      - copied unchanged from r1647584, 
manifoldcf/branches/CONNECTORS-1119/framework/crawler-ui/src/main/webapp/viewnotification.jsp
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/INotificationConnection.java
      - copied unchanged from r1647584, 
manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/INotificationConnection.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/INotificationConnectionManager.java
      - copied unchanged from r1647584, 
manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/INotificationConnectionManager.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/INotificationConnector.java
      - copied unchanged from r1647584, 
manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/INotificationConnector.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/INotificationConnectorManager.java
      - copied unchanged from r1647584, 
manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/INotificationConnectorManager.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/INotificationConnectorPool.java
      - copied unchanged from r1647584, 
manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/INotificationConnectorPool.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/NotificationConnectionManagerFactory.java
      - copied unchanged from r1647584, 
manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/NotificationConnectionManagerFactory.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/NotificationConnectorFactory.java
      - copied unchanged from r1647584, 
manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/NotificationConnectorFactory.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/NotificationConnectorManagerFactory.java
      - copied unchanged from r1647584, 
manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/NotificationConnectorManagerFactory.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/NotificationConnectorPoolFactory.java
      - copied unchanged from r1647584, 
manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/NotificationConnectorPoolFactory.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/NotificationManager.java
      - copied unchanged from r1647584, 
manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/NotificationManager.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notification/
      - copied from r1647584, 
manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notification/
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notificationconnectorpool/
      - copied from r1647584, 
manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notificationconnectorpool/
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notificationconnmgr/
      - copied from r1647584, 
manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notificationconnmgr/
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notifications/
      - copied from r1647584, 
manifoldcf/branches/CONNECTORS-1119/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/notifications/
Modified:
    manifoldcf/trunk/   (props changed)
    manifoldcf/trunk/CHANGES.txt
    manifoldcf/trunk/connectors/email/build.xml
    
manifoldcf/trunk/framework/api-service/src/main/java/org/apache/manifoldcf/apiservice/IdleCleanupThread.java
    manifoldcf/trunk/framework/buildfiles/connector-build.xml
    
manifoldcf/trunk/framework/combined-service/src/main/java/org/apache/manifoldcf/combinedservice/IdleCleanupThread.java
    
manifoldcf/trunk/framework/crawler-ui/src/main/java/org/apache/manifoldcf/crawlerui/IdleCleanupThread.java
    manifoldcf/trunk/framework/crawler-ui/src/main/webapp/editjob.jsp
    manifoldcf/trunk/framework/crawler-ui/src/main/webapp/editoutput.jsp
    manifoldcf/trunk/framework/crawler-ui/src/main/webapp/edittransformation.jsp
    manifoldcf/trunk/framework/crawler-ui/src/main/webapp/execute.jsp
    manifoldcf/trunk/framework/crawler-ui/src/main/webapp/navigation.jsp
    manifoldcf/trunk/framework/crawler-ui/src/main/webapp/viewjob.jsp
    manifoldcf/trunk/framework/example-common/connectors.xml
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/CacheKeyFactory.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobDescription.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobManager.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobDescription.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobManager.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/Jobs.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/CrawlerAgent.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/IdleCleanupThread.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/JobNotificationThread.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/JobResetThread.java
    
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/ManifoldCF.java
    
manifoldcf/trunk/framework/ui-core/src/main/native2ascii/org/apache/manifoldcf/ui/i18n/common_en_US.properties
    
manifoldcf/trunk/framework/ui-core/src/main/native2ascii/org/apache/manifoldcf/ui/i18n/common_ja_JP.properties
    
manifoldcf/trunk/framework/ui-core/src/main/native2ascii/org/apache/manifoldcf/ui/i18n/common_zh_CN.properties

Propchange: manifoldcf/trunk/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Tue Dec 23 14:46:43 2014
@@ -45,6 +45,7 @@
 /manifoldcf/branches/CONNECTORS-1100:1637693-1640317
 /manifoldcf/branches/CONNECTORS-1104:1640149-1640198
 /manifoldcf/branches/CONNECTORS-1118:1644108-1644398
+/manifoldcf/branches/CONNECTORS-1119:1645497-1647584
 
/manifoldcf/branches/CONNECTORS-120:1406712-1407974,1407982-1411043,1411049-1416451
 /manifoldcf/branches/CONNECTORS-120-1:1416450-1417056
 /manifoldcf/branches/CONNECTORS-13:1525862-1527182,1539324-1541634

Modified: manifoldcf/trunk/CHANGES.txt
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/CHANGES.txt?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- manifoldcf/trunk/CHANGES.txt (original)
+++ manifoldcf/trunk/CHANGES.txt Tue Dec 23 14:46:43 2014
@@ -3,6 +3,9 @@ $Id$
 
 ======================= 2.1-dev =====================
 
+CONNECTORS-1119: Add support for notification connectors, and
+an email notification connector.
+(Karl Wright)
 
 ======================= Release 2.0 =====================
 

Modified: manifoldcf/trunk/connectors/email/build.xml
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/connectors/email/build.xml?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- manifoldcf/trunk/connectors/email/build.xml (original)
+++ manifoldcf/trunk/connectors/email/build.xml Tue Dec 23 14:46:43 2014
@@ -35,6 +35,10 @@ limitations under the License.
             <param name="connector-label" value="EMail"/>
             <param name="connector-class" 
value="org.apache.manifoldcf.crawler.connectors.email.EmailConnector"/>
         </antcall>
+        <antcall target="general-add-notification-connector">
+            <param name="connector-label" value="EMail"/>
+            <param name="connector-class" 
value="org.apache.manifoldcf.crawler.notifications.email.EmailConnector"/>
+        </antcall>
     </target>
 
 </project>
\ No newline at end of file

Modified: 
manifoldcf/trunk/framework/api-service/src/main/java/org/apache/manifoldcf/apiservice/IdleCleanupThread.java
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/api-service/src/main/java/org/apache/manifoldcf/apiservice/IdleCleanupThread.java?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- 
manifoldcf/trunk/framework/api-service/src/main/java/org/apache/manifoldcf/apiservice/IdleCleanupThread.java
 (original)
+++ 
manifoldcf/trunk/framework/api-service/src/main/java/org/apache/manifoldcf/apiservice/IdleCleanupThread.java
 Tue Dec 23 14:46:43 2014
@@ -53,6 +53,7 @@ public class IdleCleanupThread extends T
       IThreadContext threadContext = ThreadContextFactory.make();
       
       IRepositoryConnectorPool repositoryConnectorPool = 
RepositoryConnectorPoolFactory.make(threadContext);
+      INotificationConnectorPool notificationConnectorPool = 
NotificationConnectorPoolFactory.make(threadContext);
       IOutputConnectorPool outputConnectorPool = 
OutputConnectorPoolFactory.make(threadContext);
       ITransformationConnectorPool transformationConnectorPool = 
TransformationConnectorPoolFactory.make(threadContext);
       IAuthorityConnectorPool authorityConnectorPool = 
AuthorityConnectorPoolFactory.make(threadContext);
@@ -66,6 +67,7 @@ public class IdleCleanupThread extends T
         {
           // Do the cleanup
           repositoryConnectorPool.pollAllConnectors();
+          notificationConnectorPool.pollAllConnectors();
           outputConnectorPool.pollAllConnectors();
           transformationConnectorPool.pollAllConnectors();
           authorityConnectorPool.pollAllConnectors();

Modified: manifoldcf/trunk/framework/buildfiles/connector-build.xml
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/buildfiles/connector-build.xml?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- manifoldcf/trunk/framework/buildfiles/connector-build.xml (original)
+++ manifoldcf/trunk/framework/buildfiles/connector-build.xml Tue Dec 23 
14:46:43 2014
@@ -886,6 +886,25 @@
     <target name="general-add-transformation-connector" 
depends="general-add-transformation-connector-commented,general-add-transformation-connector-non-commented,general-add-transformation-connector-proprietary-commented,general-add-transformation-connector-proprietary-non-commented">
     </target>
 
+    <target name="general-add-notification-connector-commented" 
depends="general-connector-runnable-check" unless="is-runnable">
+        <replace file="${mcf-dist}/connectors.xml" token="&lt;!-- Add your 
notification connectors here --&gt;" value="&lt;!-- Add your notification 
connectors here --&gt;&#0010;  &lt;!--notificationconnector 
name=&quot;${connector-label}&quot; 
class=&quot;${connector-class}&quot;/--&gt;"/>
+    </target>
+
+    <target name="general-add-notification-connector-non-commented" 
depends="general-connector-runnable-check" if="is-runnable">
+        <replace file="${mcf-dist}/connectors.xml" token="&lt;!-- Add your 
notification connectors here --&gt;" value="&lt;!-- Add your notification 
connectors here --&gt;&#0010;  &lt;notificationconnector 
name=&quot;${connector-label}&quot; class=&quot;${connector-class}&quot;/&gt;"/>
+    </target>
+
+    <target name="general-add-notification-connector-proprietary-commented" 
depends="general-connector-proprietary-runnable-check" 
unless="is-proprietary-runnable">
+        <replace file="${mcf-dist}/connectors-proprietary.xml" token="&lt;!-- 
Add your notification connectors here --&gt;" value="&lt;!-- Add your 
notification connectors here --&gt;&#0010;  &lt;!--notificationconnector 
name=&quot;${connector-label}&quot; 
class=&quot;${connector-class}&quot;/--&gt;"/>
+    </target>
+
+    <target 
name="general-add-notification-connector-proprietary-non-commented" 
depends="general-connector-proprietary-runnable-check" 
if="is-proprietary-runnable">
+        <replace file="${mcf-dist}/connectors-proprietary.xml" token="&lt;!-- 
Add your notification connectors here --&gt;" value="&lt;!-- Add your 
notification connectors here --&gt;&#0010;  &lt;notificationconnector 
name=&quot;${connector-label}&quot; class=&quot;${connector-class}&quot;/&gt;"/>
+    </target>
+
+    <target name="general-add-notification-connector" 
depends="general-add-notification-connector-commented,general-add-notification-connector-non-commented,general-add-notification-connector-proprietary-commented,general-add-notification-connector-proprietary-non-commented">
+    </target>
+
     <target name="general-add-repository-connector-commented" 
depends="general-connector-runnable-check" unless="is-runnable">
         <replace file="${mcf-dist}/connectors.xml" token="&lt;!-- Add your 
repository connectors here --&gt;" value="&lt;!-- Add your repository 
connectors here --&gt;&#0010;  &lt;!--repositoryconnector 
name=&quot;${connector-label}&quot; 
class=&quot;${connector-class}&quot;/--&gt;"/>
     </target>

Modified: 
manifoldcf/trunk/framework/combined-service/src/main/java/org/apache/manifoldcf/combinedservice/IdleCleanupThread.java
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/combined-service/src/main/java/org/apache/manifoldcf/combinedservice/IdleCleanupThread.java?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- 
manifoldcf/trunk/framework/combined-service/src/main/java/org/apache/manifoldcf/combinedservice/IdleCleanupThread.java
 (original)
+++ 
manifoldcf/trunk/framework/combined-service/src/main/java/org/apache/manifoldcf/combinedservice/IdleCleanupThread.java
 Tue Dec 23 14:46:43 2014
@@ -53,6 +53,7 @@ public class IdleCleanupThread extends T
       IThreadContext threadContext = ThreadContextFactory.make();
       
       IRepositoryConnectorPool repositoryConnectorPool = 
RepositoryConnectorPoolFactory.make(threadContext);
+      INotificationConnectorPool notificationConnectorPool = 
NotificationConnectorPoolFactory.make(threadContext);
       IOutputConnectorPool outputConnectorPool = 
OutputConnectorPoolFactory.make(threadContext);
       ITransformationConnectorPool transformationConnectorPool = 
TransformationConnectorPoolFactory.make(threadContext);
       IAuthorityConnectorPool authorityConnectorPool = 
AuthorityConnectorPoolFactory.make(threadContext);
@@ -66,6 +67,7 @@ public class IdleCleanupThread extends T
         {
           // Do the cleanup
           repositoryConnectorPool.pollAllConnectors();
+          notificationConnectorPool.pollAllConnectors();
           outputConnectorPool.pollAllConnectors();
           transformationConnectorPool.pollAllConnectors();
           authorityConnectorPool.pollAllConnectors();

Modified: 
manifoldcf/trunk/framework/crawler-ui/src/main/java/org/apache/manifoldcf/crawlerui/IdleCleanupThread.java
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/crawler-ui/src/main/java/org/apache/manifoldcf/crawlerui/IdleCleanupThread.java?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- 
manifoldcf/trunk/framework/crawler-ui/src/main/java/org/apache/manifoldcf/crawlerui/IdleCleanupThread.java
 (original)
+++ 
manifoldcf/trunk/framework/crawler-ui/src/main/java/org/apache/manifoldcf/crawlerui/IdleCleanupThread.java
 Tue Dec 23 14:46:43 2014
@@ -53,6 +53,7 @@ public class IdleCleanupThread extends T
       IThreadContext threadContext = ThreadContextFactory.make();
       
       IRepositoryConnectorPool repositoryConnectorPool = 
RepositoryConnectorPoolFactory.make(threadContext);
+      INotificationConnectorPool notificationConnectorPool = 
NotificationConnectorPoolFactory.make(threadContext);
       IOutputConnectorPool outputConnectorPool = 
OutputConnectorPoolFactory.make(threadContext);
       ITransformationConnectorPool transformationConnectorPool = 
TransformationConnectorPoolFactory.make(threadContext);
       IAuthorityConnectorPool authorityConnectorPool = 
AuthorityConnectorPoolFactory.make(threadContext);
@@ -66,6 +67,7 @@ public class IdleCleanupThread extends T
         {
           // Do the cleanup
           repositoryConnectorPool.pollAllConnectors();
+          notificationConnectorPool.pollAllConnectors();
           outputConnectorPool.pollAllConnectors();
           transformationConnectorPool.pollAllConnectors();
           authorityConnectorPool.pollAllConnectors();

Modified: manifoldcf/trunk/framework/crawler-ui/src/main/webapp/editjob.jsp
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/crawler-ui/src/main/webapp/editjob.jsp?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- manifoldcf/trunk/framework/crawler-ui/src/main/webapp/editjob.jsp (original)
+++ manifoldcf/trunk/framework/crawler-ui/src/main/webapp/editjob.jsp Tue Dec 
23 14:46:43 2014
@@ -33,6 +33,8 @@
        IJobManager manager = JobManagerFactory.make(threadContext);
        IRepositoryConnectionManager connMgr = 
RepositoryConnectionManagerFactory.make(threadContext);
        IRepositoryConnection[] connList = connMgr.getAllConnections();
+       INotificationConnectionManager notificationMgr = 
NotificationConnectionManagerFactory.make(threadContext);
+       INotificationConnection[] notificationList = 
notificationMgr.getAllConnections();
        IOutputConnectionManager outputMgr = 
OutputConnectionManagerFactory.make(threadContext);
        IOutputConnection[] outputList = outputMgr.getAllConnections();
        ITransformationConnectionManager transformationMgr = 
TransformationConnectionManagerFactory.make(threadContext);
@@ -40,6 +42,7 @@
 
        IOutputConnectorPool outputConnectorPool = 
OutputConnectorPoolFactory.make(threadContext);
        IRepositoryConnectorPool repositoryConnectorPool = 
RepositoryConnectorPoolFactory.make(threadContext);
+       INotificationConnectorPool notificationConnectorPool = 
NotificationConnectorPoolFactory.make(threadContext);
        ITransformationConnectorPool transformationConnectorPool = 
TransformationConnectorPoolFactory.make(threadContext);
 
        // Figure out tab name and sequence number
@@ -89,6 +92,10 @@
        int[] pipelinePrerequisites = new int[0];
        Specification[] pipelineSpecifications = new Specification[0];
        
+       String[] notificationConnectionNames = new String[0];
+       String[] notificationDescriptions = new String[0];
+       Specification[] notificationSpecifications = new Specification[0];
+       
        ArrayList scheduleRecords = new ArrayList();
 
        EnumeratedValues dayOfWeek = null;
@@ -139,6 +146,16 @@
                        pipelinePrerequisites[j] = 
job.getPipelineStagePrerequisite(j);
                        pipelineSpecifications[j] = 
job.getPipelineStageSpecification(j);
                }
+               notificationConnectionNames = new 
String[job.countNotifications()];
+               notificationDescriptions = new String[job.countNotifications()];
+               notificationSpecifications = new 
Specification[job.countNotifications()];
+               for (int j = 0; j < job.countNotifications(); j++)
+               {
+                       notificationConnectionNames[j] = 
job.getNotificationConnectionName(j);
+                       notificationDescriptions[j] = 
job.getNotificationDescription(j);
+                       notificationSpecifications[j] = 
job.getNotificationSpecification(j);
+               }
+               
                type = job.getType();
                startMethod = job.getStartMethod();
                hopcountMode = job.getHopcountMode();
@@ -204,11 +221,19 @@
        String saveCheckMethod = "checkSpecificationForSave";
        String[] pipelineCheckMethods = new 
String[pipelineConnectionNames.length];
        String[] pipelineCheckForSaveMethods = new 
String[pipelineConnectionNames.length];
+       String[] notificationCheckMethods = new 
String[notificationConnectionNames.length];
+       String[] notificationCheckForSaveMethods = new 
String[notificationConnectionNames.length];
+       
        for (int j = 0; j < pipelineConnectionNames.length; j++)
        {
                pipelineCheckMethods[j] = "unknown";
                pipelineCheckForSaveMethods[j] = "unknown";
        }
+       for (int j = 0; j < notificationConnectionNames.length; j++)
+       {
+               notificationCheckMethods[j] = "unknown";
+               notificationCheckForSaveMethods[j] = "unknown";
+       }
        
        if (connection != null)
        {
@@ -250,6 +275,20 @@
                }
        }
 
+       for (int j = 0; j < notificationConnectionNames.length; j++)
+       {
+               INotificationConnection notificationConnection = 
notificationMgr.load(notificationConnectionNames[j]);
+               if (notificationConnection != null)
+               {
+                       INotificationConnector notificationConnector = 
NotificationConnectorFactory.getConnectorNoCheck(notificationConnection.getClassName());
+                       if (notificationConnector != null)
+                       {
+                               notificationCheckMethods[j] = 
notificationConnector.getFormCheckJavascriptMethodName(1+pipelineConnectionNames.length+j);
+                               notificationCheckForSaveMethods[j] = 
notificationConnector.getFormPresaveCheckJavascriptMethodName(1+pipelineConnectionNames.length+j);
+                       }
+
+               }
+       }
 %>
 
 <?xml version="1.0" encoding="utf-8"?>
@@ -346,6 +385,16 @@
                        }
 <%
        }
+       for (int j = 0; j < notificationCheckForSaveMethods.length; j++)
+       {
+%>
+                       if (window.<%=notificationCheckForSaveMethods[j]%>)
+                       {
+                               if (<%=notificationCheckForSaveMethods[j]%>() 
== false)
+                                       return;
+                       }
+<%
+       }
 %>
                        if (window.<%=saveCheckMethod%>)
                        {
@@ -413,7 +462,28 @@
                else
                        postFormSetAnchor("pipeline_"+(n-1)+"_tag");
        }
+
+       function AppendNotification()
+       {
+               if (editjob.notification_connectionname.value == "")
+               {
+                       
alert("<%=Messages.getBodyJavascriptString(pageContext.getRequest().getLocale(),"editjob.SelectANotificationConnectionName")%>");
+                       editjob.notification_connectionname.focus();
+                       return;
+               }
+               document.editjob.notification_op.value="Add";
+               postFormSetAnchor("notification_tag");
+       }
        
+       function DeleteNotification(n)
+       {
+               eval("document.editjob.notification_"+n+"_op.value = 'Delete'");
+               if (n == 0)
+                       postFormSetAnchor("notification_tag");
+               else
+                       postFormSetAnchor("notification_"+(n-1)+"_tag");
+       }
+
        function AddScheduledTime()
        {
                if (editjob.duration.value != "" && 
!isInteger(editjob.duration.value))
@@ -460,6 +530,16 @@
                }
 <%
        }
+       for (int j = 0; j < notificationCheckMethods.length; j++)
+       {
+%>
+               if (window.<%=notificationCheckMethods[j]%>)
+               {
+                       if (<%=notificationCheckMethods[j]%>() == false)
+                               return false;
+               }
+<%
+       }
 %>
                // Check the connector part
                if (window.<%=checkMethod%>)
@@ -638,6 +718,33 @@
        
 %>
 
+<%
+       for (int j = 0; j < notificationConnectionNames.length; j++)
+       {
+               INotificationConnection notificationConnection = 
notificationMgr.load(notificationConnectionNames[j]);
+               if (notificationConnection != null)
+               {
+                       INotificationConnector notificationConnector = 
notificationConnectorPool.grab(notificationConnection);
+                       if (notificationConnector != null)
+                       {
+                               try
+                               {
+                                       
notificationConnector.outputSpecificationHeader(new 
org.apache.manifoldcf.ui.jsp.JspWrapper(out,adminprofile),pageContext.getRequest().getLocale(),notificationSpecifications[j],1+pipelineConnectionNames.length+j,tabsArray);
+                               }
+                               finally
+                               {
+                                       
notificationConnectorPool.release(notificationConnection,notificationConnector);
+                               }
+                       }
+               }
+               Integer connectionSequenceNumber = new 
Integer(1+pipelineConnectionNames.length+j);
+               while (sequenceArray.size() < tabsArray.size())
+               {
+                       sequenceArray.add(connectionSequenceNumber);
+               }
+       }
+%>
+
 </head>
 
 <body class="standardbody">
@@ -1081,7 +1188,94 @@
                                        </table>
                                </td>
                        </tr>
-                       
+
+<%
+               alreadyPresent = new HashSet<String>();
+               for (int j = 0; j < notificationConnectionNames.length; j++)
+               {
+                       alreadyPresent.add(notificationConnectionNames[j]);
+               }
+               if (notificationList.length > 0)
+               {
+%>
+                       <tr>
+                               <td colspan="1" 
class="description"><nobr><%=Messages.getBodyString(pageContext.getRequest().getLocale(),"editjob.NotificationsColon")%></nobr></td>
+                               <td class="boxcell" colspan="3">
+                                       <table class="formtable">
+                                               <tr class="formheaderrow">
+                                                       <td 
class="formcolumnheader">
+                                                               <input 
name="notification_count" type="hidden" 
value="<%=notificationConnectionNames.length%>"/>
+                                                       </td>
+                                                       <td 
class="formcolumnheader"><nobr><%=Messages.getBodyString(pageContext.getRequest().getLocale(),"editjob.StageNumber")%></nobr></td>
+                                                       <td 
class="formcolumnheader"><nobr><%=Messages.getBodyString(pageContext.getRequest().getLocale(),"editjob.NotificationDescription")%></nobr></td>
+                                                       <td 
class="formcolumnheader"><nobr><%=Messages.getBodyString(pageContext.getRequest().getLocale(),"editjob.NotificationConnectionName")%></nobr></td>
+                                               </tr>
+<%
+                       rowCounter = 0;
+                       for (int j = 0; j < notificationConnectionNames.length; 
j++)
+                       {
+                               String notificationConnectionName = 
notificationConnectionNames[j];
+                               String notificationDescription = 
notificationDescriptions[j];
+                               if (notificationDescription == null)
+                                       notificationDescription = "";
+%>
+                                               <tr class="<%=((rowCounter++ % 
2)==0)?"evenformrow":"oddformrow"%>">
+                                                       <td 
class="formcolumncell">
+                                                               <input 
name="notification_<%=j%>_op" type="hidden" value="Continue"/>
+                                                               <a 
name="notification_<%=j%>_tag"/>
+                                                               <input 
type="button" 
value="<%=Messages.getBodyString(pageContext.getRequest().getLocale(),"editjob.Delete")%>"
 
alt='<%=Messages.getBodyString(pageContext.getRequest().getLocale(),"editjob.Deletenotification")%>'
 onclick="javascript:DeleteNotification(<%=j%>);"/>
+                                                       </td>
+                                                       <td 
class="formcolumncell"><%=(j+pipelineConnectionNames.length+2)%>.</td>
+                                                       <td 
class="formcolumncell">
+                                                               <input 
name="notification_<%=j%>_description" type="text" size="30" 
value="<%=org.apache.manifoldcf.ui.util.Encoder.attributeEscape(notificationDescription)%>"/>
+                                                       </td>
+                                                       <td 
class="formcolumncell">
+                                                               
<nobr><%=org.apache.manifoldcf.ui.util.Encoder.bodyEscape(notificationConnectionName)%></nobr>
+                                                               <input 
name="notification_<%=j%>_connectionname" type="hidden" 
value="<%=org.apache.manifoldcf.ui.util.Encoder.attributeEscape(notificationConnectionName)%>"/>
+                                                       </td>
+                                               </tr>
+<%
+                       }
+                       if (notificationList.length != alreadyPresent.size())
+                       {
+%>
+                                               <tr class="formrow"><td 
class="formseparator" colspan="4"><hr/></td></tr>
+                                               <tr class="formrow">
+                                                       <td 
class="formcolumncell">
+                                                               <a 
name="notification_tag"/>
+                                                               <input 
type="button" 
value='<%=Messages.getAttributeString(pageContext.getRequest().getLocale(),"editjob.AddNotification")%>'
 
alt='<%=Messages.getAttributeString(pageContext.getRequest().getLocale(),"editjob.AddANotification")%>'
 onclick="javascript:AppendNotification();"/>
+                                                               <input 
name="notification_op" type="hidden" value="Continue"/>
+                                                       </td>
+                                                       <td 
class="formcolumncell"></td>
+                                                       <td 
class="formcolumncell">
+                                                               <input 
name="notification_description" type="text" size="30" value=""/>
+                                                       </td>
+                                                       <td 
class="formcolumncell">
+                                                               <select 
name="notification_connectionname" size="1">
+                                                                       <option 
selected="selected" value="">-- 
<%=Messages.getBodyString(pageContext.getRequest().getLocale(),"editjob.NoneSelected")%>
 --</option>
+<%
+                               for (INotificationConnection conn : 
notificationList)
+                               {
+                                       if 
(!alreadyPresent.contains(conn.getName()))
+                                       {
+%>
+                                                                       <option 
value='<%=org.apache.manifoldcf.ui.util.Encoder.attributeEscape(conn.getName())%>'><%=org.apache.manifoldcf.ui.util.Encoder.bodyEscape(conn.getName())%></option>
+<%
+                                       }
+                               }
+%>
+                                                               </select>
+                                                       </td>
+                                               </tr>
+<%
+                       }
+%>
+                                       </table>
+                               </td>
+                       </tr>
+<%
+               }
+%>
                        <tr><td class="separator" colspan="4"><hr/></td></tr>
                        
                        <tr>
@@ -1117,6 +1311,7 @@
 %>
                  <input type="hidden" name="connectionname" 
value='<%=org.apache.manifoldcf.ui.util.Encoder.attributeEscape(connectionName)%>'/>
                  <input type="hidden" name="pipeline_count" 
value="<%=pipelineConnectionNames.length%>"/>
+                 <input type="hidden" name="notification_count" 
value="<%=notificationConnectionNames.length%>"/>
 <%
                for (int j = 0; j < pipelineConnectionNames.length; j++)
                {
@@ -1131,6 +1326,18 @@
                  <input type="hidden" name="pipeline_<%=j%>_description" 
value="<%=org.apache.manifoldcf.ui.util.Encoder.attributeEscape(pipelineDescription)%>"/>
 <%
                }
+               for (int j = 0; j < notificationConnectionNames.length; j++)
+               {
+                       String notificationConnectionName = 
notificationConnectionNames[j];
+                       String notificationDescription = 
notificationDescriptions[j];
+                       if (notificationDescription == null)
+                               notificationDescription = "";
+%>
+                 <input type="hidden" 
name="notification_<%=j%>_connectionname" 
value="<%=org.apache.manifoldcf.ui.util.Encoder.attributeEscape(notificationConnectionName)%>"/>
+                 <input type="hidden" name="notification_<%=j%>_description" 
value="<%=org.apache.manifoldcf.ui.util.Encoder.attributeEscape(notificationDescription)%>"/>
+<%
+               }
+
 %>
                  <input type="hidden" name="priority" value='<%=priority%>'/>
                  <input type="hidden" name="startmethod" 
value='<%=startMethod%>'/>
@@ -1656,6 +1863,26 @@
                                }
                        }
                }
+       }
+       
+       for (int j = 0; j < notificationConnectionNames.length; j++)
+       {
+               INotificationConnection notificationConnection = 
notificationMgr.load(notificationConnectionNames[j]);
+               if (notificationConnection != null)
+               {
+                       INotificationConnector notificationConnector = 
notificationConnectorPool.grab(notificationConnection);
+                       if (notificationConnector != null)
+                       {
+                               try
+                               {
+                                       
notificationConnector.outputSpecificationBody(new 
org.apache.manifoldcf.ui.jsp.JspWrapper(out,adminprofile),pageContext.getRequest().getLocale(),notificationSpecifications[j],1+pipelineConnectionNames.length+j,tabSequenceInt,tabName);
+                               }
+                               finally
+                               {
+                                       
notificationConnectorPool.release(notificationConnection,notificationConnector);
+                               }
+                       }
+               }
        }
 
 %>

Modified: manifoldcf/trunk/framework/crawler-ui/src/main/webapp/editoutput.jsp
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/crawler-ui/src/main/webapp/editoutput.jsp?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- manifoldcf/trunk/framework/crawler-ui/src/main/webapp/editoutput.jsp 
(original)
+++ manifoldcf/trunk/framework/crawler-ui/src/main/webapp/editoutput.jsp Tue 
Dec 23 14:46:43 2014
@@ -232,7 +232,7 @@
        {
 %>
        <p 
class="windowtitle"><%=Messages.getBodyString(pageContext.getRequest().getLocale(),"editoutput.EditOutputConnection")%></p>
-       <table class="displaytable"><tr><td class="message">No output 
connectors registered</td></tr></table>
+       <table class="displaytable"><tr><td 
class="message"><%=Messages.getBodyString(pageContext.getRequest().getLocale(),"editoutput.NoOutputConnectorsRegistered")%></td></tr></table>
 <%
        }
        else

Modified: 
manifoldcf/trunk/framework/crawler-ui/src/main/webapp/edittransformation.jsp
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/crawler-ui/src/main/webapp/edittransformation.jsp?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- 
manifoldcf/trunk/framework/crawler-ui/src/main/webapp/edittransformation.jsp 
(original)
+++ 
manifoldcf/trunk/framework/crawler-ui/src/main/webapp/edittransformation.jsp 
Tue Dec 23 14:46:43 2014
@@ -231,8 +231,8 @@
        if (set.getRowCount() == 0)
        {
 %>
-       <p 
class="windowtitle"><%=Messages.getBodyString(pageContext.getRequest().getLocale(),"edittransformation.EditTransformationConnection")%></p>
-       <table class="displaytable"><tr><td class="message">No transformation 
connectors registered</td></tr></table>
+       <p 
class="windowtitle"><%=Messages.getBodyString(pageContext.getRequest().getLocale(),"edittransformation.EditTransformationConnection2")%></p>
+       <table class="displaytable"><tr><td 
class="message"><%=Messages.getBodyString(pageContext.getRequest().getLocale(),"edittransformation.NoTransformationConnectorsRegistered")%></td></tr></table>
 <%
        }
        else

Modified: manifoldcf/trunk/framework/crawler-ui/src/main/webapp/execute.jsp
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/crawler-ui/src/main/webapp/execute.jsp?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- manifoldcf/trunk/framework/crawler-ui/src/main/webapp/execute.jsp (original)
+++ manifoldcf/trunk/framework/crawler-ui/src/main/webapp/execute.jsp Tue Dec 
23 14:46:43 2014
@@ -47,6 +47,7 @@
                IJobManager manager = JobManagerFactory.make(threadContext);
                IAuthorityGroupManager authGroupManager = 
AuthorityGroupManagerFactory.make(threadContext);
                IRepositoryConnectionManager connManager = 
RepositoryConnectionManagerFactory.make(threadContext);
+               INotificationConnectionManager notificationManager = 
NotificationConnectionManagerFactory.make(threadContext);
                IAuthorityConnectionManager authConnManager = 
AuthorityConnectionManagerFactory.make(threadContext);
                IMappingConnectionManager mappingConnManager = 
MappingConnectionManagerFactory.make(threadContext);
                IOutputConnectionManager outputManager = 
OutputConnectionManagerFactory.make(threadContext);
@@ -55,6 +56,7 @@
                IOutputConnectorPool outputConnectorPool = 
OutputConnectorPoolFactory.make(threadContext);
                ITransformationConnectorPool transformationConnectorPool = 
TransformationConnectorPoolFactory.make(threadContext);
                IRepositoryConnectorPool repositoryConnectorPool = 
RepositoryConnectorPoolFactory.make(threadContext);
+               INotificationConnectorPool notificationConnectorPool = 
NotificationConnectorPoolFactory.make(threadContext);
                
                String type = variableContext.getParameter("type");
                String op = variableContext.getParameter("op");
@@ -865,6 +867,121 @@
 <%
                        }
                }
+               else if (type != null && op != null && 
type.equals("notification"))
+               {
+                       // -- Notification connection editing operations --
+                       if (op.equals("Save") || op.equals("Continue"))
+                       {
+                               try
+                               {
+                                       // Set up a connection object that is a 
merge of an existing connection object plus what was posted.
+                                       INotificationConnection connection = 
null;
+                                       boolean isNew = true;
+                                       String x = 
variableContext.getParameter("isnewconnection");
+                                       if (x != null)
+                                               isNew = x.equals("true");
+
+                                       String connectionName = 
variableContext.getParameter("connname");
+                                       // If the connectionname is not null, 
load the connection description and prepopulate everything with what comes from 
it.
+                                       if (connectionName != null && 
connectionName.length() > 0 && !isNew)
+                                       {
+                                               connection = 
notificationManager.load(connectionName);
+                                       }
+                                       
+                                       if (connection == null)
+                                       {
+                                               connection = 
notificationManager.create();
+                                               if (connectionName != null && 
connectionName.length() > 0)
+                                                       
connection.setName(connectionName);
+                                       }
+
+                                       // Gather all the data from the form.
+                                       connection.setIsNew(isNew);
+                                       x = 
variableContext.getParameter("description");
+                                       if (x != null)
+                                               connection.setDescription(x);
+                                       x = 
variableContext.getParameter("classname");
+                                       if (x != null)
+                                               connection.setClassName(x);
+                                       x = 
variableContext.getParameter("maxconnections");
+                                       if (x != null && x.length() > 0)
+                                               
connection.setMaxConnections(Integer.parseInt(x));
+
+                                       String error = 
NotificationConnectorFactory.processConfigurationPost(threadContext,connection.getClassName(),variableContext,pageContext.getRequest().getLocale(),connection.getConfigParams());
+                                       
+                                       if (error != null)
+                                       {
+                                               
variableContext.setParameter("text",error);
+                                               
variableContext.setParameter("target","listnotifications.jsp");
+%>
+                                               <jsp:forward page="error.jsp"/>
+<%
+                                       }
+                                       
+                                       if (op.equals("Continue"))
+                                       {
+                                               
threadContext.save("ConnectionObject",connection);
+%>
+                                               <jsp:forward 
page="editnotification.jsp"/>
+<%
+                                       }
+                                       else if (op.equals("Save"))
+                                       {
+                                               
notificationManager.save(connection);
+                                               
variableContext.setParameter("connname",connectionName);
+%>
+                                               <jsp:forward 
page="viewnotification.jsp"/>
+<%
+                                       }
+                               }
+                               catch (ManifoldCFException e)
+                               {
+                                       e.printStackTrace();
+                                       
variableContext.setParameter("text",e.getMessage());
+                                       
variableContext.setParameter("target","listnotifications.jsp");
+%>
+                                       <jsp:forward page="error.jsp"/>
+<%
+                               }
+                       }
+                       else if (op.equals("Delete"))
+                       {
+                               try
+                               {
+                                       String connectionName = 
variableContext.getParameter("connname");
+                                       if (connectionName == null)
+                                               throw new 
ManifoldCFException("Missing connection parameter");
+                                       
notificationManager.delete(connectionName);
+%>
+                                       <jsp:forward 
page="listnotifications.jsp"/>
+<%
+                               }
+                               catch (ManifoldCFException e)
+                               {
+                                       e.printStackTrace();
+                                       
variableContext.setParameter("text",e.getMessage());
+                                       
variableContext.setParameter("target","listnotifications.jsp");
+%>
+                                       <jsp:forward page="error.jsp"/>
+<%
+                               }
+                       }
+                       else if (op.equals("Cancel"))
+                       {
+%>
+                               <jsp:forward page="listnotifications.jsp"/>
+<%
+                       }
+                       else
+                       {
+                               // Error
+                               variableContext.setParameter("text","Illegal 
parameter to notification connection execution page");
+                               
variableContext.setParameter("target","listnotifications.jsp");
+%>
+                               <jsp:forward page="error.jsp"/>
+<%
+                       }
+               }
                else if (type != null && op != null && type.equals("job"))
                {
                        // -- Job editing operations --
@@ -918,6 +1035,21 @@
                                                        
job.addPipelineStage(precedent, isOutput, connectionName, description);
                                                }
                                        }
+                                       x = 
variableContext.getParameter("notification_count");
+                                       if (x != null)
+                                       {
+                                               // Do we need to keep the old 
specifications around, or can we destroy them?
+                                               // Not clear that retention is 
required., so I'm not wasting time trying to implement that.
+                                               int count = Integer.parseInt(x);
+                                               job.clearNotifications();
+                                               for (int j = 0; j < count; j++)
+                                               {
+                                                       // Gather everything 
first; we'll look at edits later
+                                                       String connectionName = 
variableContext.getParameter("notification_"+j+"_connectionname");
+                                                       String description = 
variableContext.getParameter("notification_"+j+"_description");
+                                                       
job.addNotification(connectionName, description);
+                                               }
+                                       }
 
                                        x = 
variableContext.getParameter("schedulerecords");
                                        String[] y;
@@ -1238,6 +1370,34 @@
                                                }
                                        }
 
+                                       for (int j = 0; j < 
job.countNotifications(); j++)
+                                       {
+                                               INotificationConnection 
notificationConnection = 
notificationManager.load(job.getNotificationConnectionName(j));
+                                               if (notificationConnection != 
null)
+                                               {
+                                                       INotificationConnector 
notificationConnector = notificationConnectorPool.grab(notificationConnection);
+                                                       if 
(notificationConnector != null)
+                                                       {
+                                                               try
+                                                               {
+                                                                       String 
error = 
notificationConnector.processSpecificationPost(variableContext,pageContext.getRequest().getLocale(),job.getNotificationSpecification(j),1+job.countPipelineStages()+j);
+                                                                       if 
(error != null)
+                                                                       {
+                                                                               
variableContext.setParameter("text",error);
+                                                                               
variableContext.setParameter("target","listjobs.jsp");
+%>
+                                                                       
<jsp:forward page="error.jsp"/>
+<%
+                                                                       }
+                                                               }
+                                                               finally
+                                                               {
+                                                                       
notificationConnectorPool.release(notificationConnection,notificationConnector);
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       
                                        // Now, after gathering is complete, 
consider doing changes to the pipeline.
                                        int currentStage = 0;
                                        for (int j = 0; j < 
job.countPipelineStages(); j++)
@@ -1268,6 +1428,7 @@
                                                else
                                                        currentStage++;
                                        }
+
                                        x = 
variableContext.getParameter("output_op");
                                        if (x != null && x.equals("Add"))
                                        {
@@ -1278,6 +1439,28 @@
                                                
job.addPipelineStage(precedent,true,connectionName,description);
                                        }
                                        
+                                       currentStage = 0;
+                                       for (int j = 0; j < 
job.countNotifications(); j++)
+                                       {
+                                               // Look at the operation
+                                               x = 
variableContext.getParameter("notification_"+j+"_op");
+                                               if (x != null && 
x.equals("Delete"))
+                                               {
+                                                       // Delete this pipeline 
stage (and rewire other stages according to rules)
+                                                       
job.deleteNotification(currentStage);
+                                               }
+                                               else
+                                                       currentStage++;
+                                       }
+                                       x = 
variableContext.getParameter("notification_op");
+                                       if (x != null && x.equals("Add"))
+                                       {
+                                               // Append a new stage at the end
+                                               String connectionName = 
variableContext.getParameter("notification_connectionname");
+                                               String description = 
variableContext.getParameter("notification_description");
+                                               
job.addNotification(connectionName,description);
+                                       }
+
                                        if (op.equals("Continue"))
                                        {
                                                
threadContext.save("JobObject",job);

Modified: manifoldcf/trunk/framework/crawler-ui/src/main/webapp/navigation.jsp
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/crawler-ui/src/main/webapp/navigation.jsp?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- manifoldcf/trunk/framework/crawler-ui/src/main/webapp/navigation.jsp 
(original)
+++ manifoldcf/trunk/framework/crawler-ui/src/main/webapp/navigation.jsp Tue 
Dec 23 14:46:43 2014
@@ -54,6 +54,9 @@
        <li class="menuitem">
                <nobr><a class="menulink" href="listconnections.jsp" 
alt="<%=Messages.getAttributeString(pageContext.getRequest().getLocale(),"navigation.Listrepositoryconnections")%>"><%=Messages.getBodyString(pageContext.getRequest().getLocale(),"navigation.ListRepositoryConnections")%></a></nobr>
        </li>
+       <li class="menuitem">
+               <nobr><a class="menulink" href="listnotifications.jsp" 
alt="<%=Messages.getAttributeString(pageContext.getRequest().getLocale(),"navigation.Listnotificationconnections")%>"><%=Messages.getBodyString(pageContext.getRequest().getLocale(),"navigation.ListNotificationConnections")%></a></nobr>
+       </li>
 </ul>
 <p 
class="menumain"><nobr><%=Messages.getBodyString(pageContext.getRequest().getLocale(),"navigation.Jobs")%></nobr></p>
 <ul class="menusecond">

Modified: manifoldcf/trunk/framework/crawler-ui/src/main/webapp/viewjob.jsp
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/crawler-ui/src/main/webapp/viewjob.jsp?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- manifoldcf/trunk/framework/crawler-ui/src/main/webapp/viewjob.jsp (original)
+++ manifoldcf/trunk/framework/crawler-ui/src/main/webapp/viewjob.jsp Tue Dec 
23 14:46:43 2014
@@ -82,10 +82,12 @@
        IJobManager manager = JobManagerFactory.make(threadContext);
         IOutputConnectionManager outputManager = 
OutputConnectionManagerFactory.make(threadContext);
        IRepositoryConnectionManager connManager = 
RepositoryConnectionManagerFactory.make(threadContext);
+       INotificationConnectionManager notificationManager = 
NotificationConnectionManagerFactory.make(threadContext);
        ITransformationConnectionManager transformationManager = 
TransformationConnectionManagerFactory.make(threadContext);
 
        IOutputConnectorPool outputConnectorPool = 
OutputConnectorPoolFactory.make(threadContext);
        IRepositoryConnectorPool repositoryConnectorPool = 
RepositoryConnectorPoolFactory.make(threadContext);
+       INotificationConnectorPool notificationConnectorPool = 
NotificationConnectorPoolFactory.make(threadContext);
        ITransformationConnectorPool transformationConnectorPool = 
TransformationConnectorPoolFactory.make(threadContext);
 
        String jobID = variableContext.getParameter("jobid");
@@ -204,6 +206,37 @@
                                </td>
                        </tr>
                        <tr>
+                               <td class="description" 
colspan="1"><nobr><%=Messages.getBodyString(pageContext.getRequest().getLocale(),"viewjob.NotificationsColon")%></nobr></td>
+                               <td class="boxcell" colspan="3">
+                                       <table class="formtable">
+                                               <tr class="formheaderrow">
+                                                       <td 
class="formcolumnheader"><nobr><%=Messages.getBodyString(pageContext.getRequest().getLocale(),"viewjob.StageNumber")%></nobr></td>
+                                                       <td 
class="formcolumnheader"><nobr><%=Messages.getBodyString(pageContext.getRequest().getLocale(),"viewjob.NotificationDescription")%></nobr></td>
+                                                       <td 
class="formcolumnheader"><nobr><%=Messages.getBodyString(pageContext.getRequest().getLocale(),"viewjob.NotificationConnectionName")%></nobr></td>
+                                               </tr>
+<%
+               for (int j = 0; j < job.countNotifications(); j++)
+               {
+%>
+                                               <tr class="<%=((rowCounter++ % 
2)==0)?"evenformrow":"oddformrow"%>">
+                                                       <td 
class="formcolumncell"><%=(j+job.countPipelineStages()+2)%>.</td>
+                                                       <td 
class="formcolumncell"><%=(job.getNotificationDescription(j)!=null)?org.apache.manifoldcf.ui.util.Encoder.bodyEscape(job.getNotificationDescription(j)):""%></td>
+                                                       <td 
class="formcolumncell"><%=org.apache.manifoldcf.ui.util.Encoder.bodyEscape(job.getNotificationConnectionName(j))%></td>
+                                               </tr>
+<%
+               }
+               if (job.countNotifications() == 0)
+               {
+%>
+                                               <tr class="formrow"><td 
class="formcolumnmessage" 
colspan="3"><%=Messages.getBodyString(pageContext.getRequest().getLocale(),"viewjob.NoNotificationConnections")%></td></tr>
+<%
+               }
+%>
+                                       </table>
+                               </td>
+                       </tr>
+
+                       <tr>
                                <td class="separator" colspan="4"><hr/></td>
                        </tr>
                        <tr>
@@ -705,6 +738,39 @@
                        </tr>
 <%
                }
+               
+               for (int j = 0; j < job.countNotifications(); j++)
+               {
+%>
+                       <tr>
+                               <td class="separator" colspan="4"><hr/></td>
+                       </tr>
+                       <tr>
+                               <td class="message" 
colspan="4"><%=(j+job.countPipelineStages()+2)%>.</td>
+                       </tr>
+                       <tr>
+                               <td colspan="4">
+<%
+                       Specification os = job.getNotificationSpecification(j);
+                       INotificationConnection thisConnection = 
notificationManager.load(job.getNotificationConnectionName(j));
+                       INotificationConnector notificationConnector = 
notificationConnectorPool.grab(thisConnection);
+                       if (notificationConnector != null)
+                       {
+                               try
+                               {
+                                       
notificationConnector.viewSpecification(new 
org.apache.manifoldcf.ui.jsp.JspWrapper(out,adminprofile),pageContext.getRequest().getLocale(),os,1+job.countPipelineStages()+j);
+                               }
+                               finally
+                               {
+                                       
notificationConnectorPool.release(thisConnection,notificationConnector);
+                               }
+                       }
+%>
+                               </td>
+                       </tr>
+<%
+               }
+
 %>
                        <tr>
                                <td class="separator" colspan="4"><hr/></td>

Modified: manifoldcf/trunk/framework/example-common/connectors.xml
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/example-common/connectors.xml?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- manifoldcf/trunk/framework/example-common/connectors.xml (original)
+++ manifoldcf/trunk/framework/example-common/connectors.xml Tue Dec 23 
14:46:43 2014
@@ -36,5 +36,7 @@
     <!-- Add your authority connectors here -->
     
     <!-- Add your repository connectors here -->
-    
+
+    <!-- Add your notification connectors here -->
+
 </connectors>

Modified: 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/CacheKeyFactory.java
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/CacheKeyFactory.java?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/CacheKeyFactory.java
 (original)
+++ 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/CacheKeyFactory.java
 Tue Dec 23 14:46:43 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/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobDescription.java
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobDescription.java?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobDescription.java
 (original)
+++ 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobDescription.java
 Tue Dec 23 14:46:43 2014
@@ -141,6 +141,53 @@ public interface IJobDescription
   *@return the newly-created output specification.
   */
   public Specification insertPipelineStage(int index, boolean isOutput, String 
pipelineStageConnectionName, String pipelineStageDescription);
+
+  /** Clear notification connections.
+  */
+  public void clearNotifications();
+  
+  /** Add a notification.
+  *@param notificationConnectionName is the name of the notification 
connection to add.
+  *@param notificationDescription is a description of the notification being 
added.
+  *@return the empty specification for this notification.
+  */
+  public Specification addNotification(String notificationConnectionName, 
String notificationDescription);
+  
+  /** Get a count of pipeline connections.
+  *@return the current number of pipeline connections.
+  */
+  public int countNotifications();
+  
+  /** Get a specific notification connection name.
+  *@param index is the index of the notification whose connection name to get.
+  *@return the name of the connection.
+  */
+  public String getNotificationConnectionName(int index);
+
+  /** Get a specific notification description.
+  *@param index is the index of the notification whose description to get.
+  *@return the name of the connection.
+  */
+  public String getNotificationDescription(int index);
+
+  /** Get a specific notification specification.
+  *@param index is the index of the notification whose specification is needed.
+  *@return the specification for the connection.
+  */
+  public Specification getNotificationSpecification(int index);
+
+  /** Delete a notification.
+  *@param index is the index of the notification to delete.
+  */
+  public void deleteNotification(int index);
+  
+  /** Insert a new notification.
+  *@param index is the index to insert pipeline stage before
+  *@param notificationConnectionName is the connection name.
+  *@param notificationDescription is the description.
+  *@return the newly-created output specification.
+  */
+  public Specification insertNotification(int index, String 
notificationConnectionName, String notificationDescription);
   
   /** Set the job type.
   *@param type is the type (as an integer).

Modified: 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobManager.java
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobManager.java?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobManager.java
 (original)
+++ 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobManager.java
 Tue Dec 23 14:46:43 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.
@@ -966,12 +973,35 @@ public interface IJobManager
   public void noteConnectorRegistration(String[] connectionNames)
     throws ManifoldCFException;
 
+  /**  Note the deregistration of a notification connector used by the 
specified connections.
+  * This method will be called when the connector is deregistered.  Jobs that 
use these connections
+  *  must therefore enter appropriate states.
+  *@param connectionNames is the set of connection names.
+  */
+  public void noteNotificationConnectorDeregistration(String[] connectionNames)
+    throws ManifoldCFException;
+
+  /** Note the registration of a notification connector used by the specified 
connections.
+  * This method will be called when a connector is registered, on which the 
specified
+  * connections depend.
+  *@param connectionNames is the set of connection names.
+  */
+  public void noteNotificationConnectorRegistration(String[] connectionNames)
+    throws ManifoldCFException;
+
   /** Note a change in connection configuration.
   * This method will be called whenever a connection's configuration is 
modified, or when an external repository change
   * is signalled.
   */
   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/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobDescription.java
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobDescription.java?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobDescription.java
 (original)
+++ 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobDescription.java
 Tue Dec 23 14:46:43 2014
@@ -44,6 +44,7 @@ public class JobDescription implements I
   protected String description = null;
   protected String connectionName = null;
   protected final List<PipelineStage> pipelineStages = new 
ArrayList<PipelineStage>();
+  protected final List<Notification> notifications = new 
ArrayList<Notification>();
   protected int type = TYPE_CONTINUOUS;
   protected int startMethod = START_WINDOWBEGIN;
   protected int priority = 5;
@@ -100,6 +101,12 @@ public class JobDescription implements I
         pipelineStage.getDescription(),
         pipelineStage.getSpecification().duplicate(readOnly)));
     }
+    for (Notification notification : notifications)
+    {
+      rval.notifications.add(new Notification(notification.getConnectionName(),
+        notification.getDescription(),
+        notification.getSpecification().duplicate(readOnly)));
+    }
     rval.description = description;
     rval.type = type;
     // No direct modification of this object is possible
@@ -135,6 +142,10 @@ public class JobDescription implements I
     {
       pipelineStage.getSpecification().makeReadOnly();
     }
+    for (Notification notification : notifications)
+    {
+      notification.getSpecification().makeReadOnly();
+    }
     documentSpecification.makeReadOnly();
   }
 
@@ -333,6 +344,8 @@ public class JobDescription implements I
   @Override
   public void deletePipelineStage(int index)
   {
+    if (readOnly)
+      throw new IllegalStateException("Attempt to change read-only object");
     PipelineStage ps = pipelineStages.remove(index);
     int stage = index;
     while (stage < pipelineStages.size())
@@ -342,6 +355,100 @@ public class JobDescription implements I
     }
   }
 
+  /** Clear notification connections.
+  */
+  @Override
+  public void clearNotifications()
+  {
+    if (readOnly)
+      throw new IllegalStateException("Attempt to change read-only object");
+    notifications.clear();
+  }
+  
+  /** Add a notification.
+  *@param notificationConnectionName is the name of the notification 
connection to add.
+  *@param notificationDescription is a description of the notification being 
added.
+  *@return the empty specification for this notification.
+  */
+  @Override
+  public Specification addNotification(String notificationConnectionName, 
String notificationDescription)
+  {
+    if (readOnly)
+      throw new IllegalStateException("Attempt to change read-only object");
+    Notification ps = new 
Notification(notificationConnectionName,notificationDescription);
+    notifications.add(ps);
+    return ps.getSpecification();
+  }
+  
+  /** Get a count of pipeline connections.
+  *@return the current number of pipeline connections.
+  */
+  @Override
+  public int countNotifications()
+  {
+    return notifications.size();
+  }
+  
+  /** Get a specific notification connection name.
+  *@param index is the index of the notification whose connection name to get.
+  *@return the name of the connection.
+  */
+  @Override
+  public String getNotificationConnectionName(int index)
+  {
+    return notifications.get(index).getConnectionName();
+  }
+
+  /** Get a specific notification description.
+  *@param index is the index of the notification whose description to get.
+  *@return the name of the connection.
+  */
+  @Override
+  public String getNotificationDescription(int index)
+  {
+    return notifications.get(index).getDescription();
+  }
+
+  /** Get a specific notification specification.
+  *@param index is the index of the notification whose specification is needed.
+  *@return the specification for the connection.
+  */
+  @Override
+  public Specification getNotificationSpecification(int index)
+  {
+    return notifications.get(index).getSpecification();
+  }
+
+  /** Delete a notification.
+  *@param index is the index of the notification to delete.
+  */
+  @Override
+  public void deleteNotification(int index)
+  {
+    if (readOnly)
+      throw new IllegalStateException("Attempt to change read-only object");
+    notifications.remove(index);
+  }
+  
+  /** Insert a new notification.
+  *@param index is the index to insert pipeline stage before
+  *@param notificationConnectionName is the connection name.
+  *@param notificationDescription is the description.
+  *@return the newly-created output specification.
+  */
+  @Override
+  public Specification insertNotification(int index, String 
notificationConnectionName, String notificationDescription)
+  {
+    if (readOnly)
+      throw new IllegalStateException("Attempt to change read-only object");
+    // What we do here depends on the kind of stage we're inserting.
+    // Both kinds take the current stage's prerequisite as their own.  But 
what happens to the current stage will
+    // differ as to whether its reference changes or not.
+    Notification ps = new 
Notification(notificationConnectionName,notificationDescription);
+    notifications.add(index,ps);
+    return ps.getSpecification();
+  }
+  
   /** Set the job type.
   *@param type is the type (as an integer).
   */
@@ -602,6 +709,43 @@ public class JobDescription implements I
     hopcountMode = mode;
   }
 
+  protected static class Notification
+  {
+    protected final String connectionName;
+    protected final String description;
+    protected final Specification specification;
+    
+    public Notification(String connectionName, String description)
+    {
+      this.connectionName = connectionName;
+      this.description = description;
+      this.specification = new Specification();
+    }
+
+    public Notification(String connectionName, String description, 
Specification spec)
+    {
+      this.connectionName = connectionName;
+      this.description = description;
+      this.specification = spec;
+    }
+
+    public Specification getSpecification()
+    {
+      return specification;
+    }
+    
+    public String getConnectionName()
+    {
+      return connectionName;
+    }
+    
+    public String getDescription()
+    {
+      return description;
+    }
+
+  }
+  
   protected static class PipelineStage
   {
     protected int prerequisiteStage;

Modified: 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobManager.java
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobManager.java?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobManager.java
 (original)
+++ 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobManager.java
 Tue Dec 23 14:46:43 2014
@@ -46,6 +46,7 @@ public class JobManager implements IJobM
   protected final IDBInterface database;
   protected final IOutputConnectionManager outputMgr;
   protected final IRepositoryConnectionManager connectionMgr;
+  protected final INotificationConnectionManager notificationMgr;
   protected final ITransformationConnectionManager transformationMgr;
   
   protected final IOutputConnectorManager outputConnectorMgr;
@@ -80,6 +81,7 @@ public class JobManager implements IJobM
     eventManager = new EventManager(database);
     outputMgr = OutputConnectionManagerFactory.make(threadContext);
     connectionMgr = RepositoryConnectionManagerFactory.make(threadContext);
+    notificationMgr = NotificationConnectionManagerFactory.make(threadContext);
     transformationMgr = 
TransformationConnectionManagerFactory.make(threadContext);
     outputConnectorMgr = OutputConnectorManagerFactory.make(threadContext);
     connectorMgr = ConnectorManagerFactory.make(threadContext);
@@ -96,7 +98,8 @@ public class JobManager implements IJobM
   {
     
jobs.install(transformationMgr.getTableName(),transformationMgr.getConnectionNameColumn(),
       outputMgr.getTableName(),outputMgr.getConnectionNameColumn(),
-      connectionMgr.getTableName(),connectionMgr.getConnectionNameColumn());
+      connectionMgr.getTableName(),connectionMgr.getConnectionNameColumn(),
+      
notificationMgr.getTableName(),notificationMgr.getConnectionNameColumn());
     jobQueue.install(jobs.getTableName(),jobs.idField);
     hopCount.install(jobs.getTableName(),jobs.idField);
     carryDown.install(jobs.getTableName(),jobs.idField);
@@ -388,6 +391,124 @@ public class JobManager implements IJobM
     }
   }
 
+  /**  Note the deregistration of a notification connector used by the 
specified connections.
+  * This method will be called when the connector is deregistered.  Jobs that 
use these connections
+  *  must therefore enter appropriate states.
+  *@param connectionNames is the set of connection names.
+  */
+  @Override
+  public void noteNotificationConnectorDeregistration(String[] connectionNames)
+    throws ManifoldCFException
+  {
+    // For each connection, find the corresponding list of jobs.  From these 
jobs, we want the job id and the status.
+    List<String> list = new ArrayList<String>();
+    int maxCount = database.findConjunctionClauseMax(new 
ClauseDescription[]{});
+    int currentCount = 0;
+    int i = 0;
+    while (i < connectionNames.length)
+    {
+      if (currentCount == maxCount)
+      {
+        noteNotificationConnectionDeregistration(list);
+        list.clear();
+        currentCount = 0;
+      }
+
+      list.add(connectionNames[i++]);
+      currentCount++;
+    }
+    if (currentCount > 0)
+      noteNotificationConnectionDeregistration(list);
+  }
+
+  /** Note deregistration for a batch of notification connection names.
+  */
+  protected void noteNotificationConnectionDeregistration(List<String> list)
+    throws ManifoldCFException
+  {
+    // Query for the matching jobs, and then for each job potentially adjust 
the state
+    Long[] jobIDs = jobs.findJobsMatchingNotifications(list);
+    if (jobIDs.length == 0)
+      return;
+
+    StringBuilder query = new StringBuilder();
+    ArrayList newList = new ArrayList();
+    
+    query.append("SELECT 
").append(jobs.idField).append(",").append(jobs.statusField)
+      .append(" FROM ").append(jobs.getTableName()).append(" WHERE ")
+      .append(database.buildConjunctionClause(newList,new ClauseDescription[]{
+        new MultiClause(jobs.idField,jobIDs)}))
+      .append(" FOR UPDATE");
+    IResultSet set = database.performQuery(query.toString(),newList,null,null);
+    int i = 0;
+    while (i < set.getRowCount())
+    {
+      IResultRow row = set.getRow(i++);
+      Long jobID = (Long)row.getValue(jobs.idField);
+      int statusValue = 
jobs.stringToStatus((String)row.getValue(jobs.statusField));
+      jobs.noteNotificationConnectorDeregistration(jobID,statusValue);
+    }
+ }
+
+  /** Note the registration of a notification connector used by the specified 
connections.
+  * This method will be called when a connector is registered, on which the 
specified
+  * connections depend.
+  *@param connectionNames is the set of connection names.
+  */
+  @Override
+  public void noteNotificationConnectorRegistration(String[] connectionNames)
+    throws ManifoldCFException
+  {
+    // For each connection, find the corresponding list of jobs.  From these 
jobs, we want the job id and the status.
+    List<String> list = new ArrayList<String>();
+    int maxCount = database.findConjunctionClauseMax(new 
ClauseDescription[]{});
+    int currentCount = 0;
+    int i = 0;
+    while (i < connectionNames.length)
+    {
+      if (currentCount == maxCount)
+      {
+        noteNotificationConnectionRegistration(list);
+        list.clear();
+        currentCount = 0;
+      }
+
+      list.add(connectionNames[i++]);
+      currentCount++;
+    }
+    if (currentCount > 0)
+      noteNotificationConnectionRegistration(list);
+  }
+
+  /** Note registration for a batch of connection names.
+  */
+  protected void noteNotificationConnectionRegistration(List<String> list)
+    throws ManifoldCFException
+  {
+    // Query for the matching jobs, and then for each job potentially adjust 
the state
+    Long[] jobIDs = jobs.findJobsMatchingNotifications(list);
+    if (jobIDs.length == 0)
+      return;
+
+    StringBuilder query = new StringBuilder();
+    ArrayList newList = new ArrayList();
+    
+    query.append("SELECT 
").append(jobs.idField).append(",").append(jobs.statusField)
+      .append(" FROM ").append(jobs.getTableName()).append(" WHERE ")
+      .append(database.buildConjunctionClause(newList,new ClauseDescription[]{
+        new MultiClause(jobs.idField,jobIDs)}))
+      .append(" FOR UPDATE");
+    IResultSet set = database.performQuery(query.toString(),newList,null,null);
+    int i = 0;
+    while (i < set.getRowCount())
+    {
+      IResultRow row = set.getRow(i++);
+      Long jobID = (Long)row.getValue(jobs.idField);
+      int statusValue = 
jobs.stringToStatus((String)row.getValue(jobs.statusField));
+      jobs.noteNotificationConnectorDeregistration(jobID,statusValue);
+    }
+  }
+
   /**  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
   *  must therefore enter appropriate states.
@@ -635,6 +756,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 +960,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/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/Jobs.java
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/Jobs.java?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/Jobs.java
 (original)
+++ 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/Jobs.java
 Tue Dec 23 14:46:43 2014
@@ -345,6 +345,7 @@ public class Jobs extends org.apache.man
   protected final ScheduleManager scheduleManager;
   protected final HopFilterManager hopFilterManager;
   protected final PipelineManager pipelineManager;
+  protected final NotificationManager notificationManager;
   
   protected final IOutputConnectionManager outputMgr;
   protected final IRepositoryConnectionManager connectionMgr;
@@ -366,6 +367,7 @@ public class Jobs extends org.apache.man
     scheduleManager = new ScheduleManager(threadContext,database);
     hopFilterManager = new HopFilterManager(threadContext,database);
     pipelineManager = new PipelineManager(threadContext,database);
+    notificationManager = new NotificationManager(threadContext,database);
     
     cacheManager = CacheManagerFactory.make(threadContext);
     lockManager = LockManagerFactory.make(threadContext);
@@ -379,7 +381,8 @@ public class Jobs extends org.apache.man
   */
   public void install(String transTableName, String transNameField,
     String outputTableName, String outputNameField,
-    String connectionTableName, String connectionNameField)
+    String connectionTableName, String connectionNameField,
+    String notificationConnectionTableName, String 
notificationConnectionNameField)
     throws ManifoldCFException
   {
     // Standard practice: Have a loop around everything, in case upgrade needs 
it.
@@ -442,6 +445,7 @@ public class Jobs extends org.apache.man
           
pipelineManager.writeOutputStage(id,outputConnectionName,outputConnectionSpec);
         }
       }
+      
notificationManager.install(getTableName(),idField,notificationConnectionTableName,notificationConnectionNameField);
       scheduleManager.install(getTableName(),idField);
       hopFilterManager.install(getTableName(),idField);
 
@@ -497,6 +501,7 @@ public class Jobs extends org.apache.man
     {
       hopFilterManager.deinstall();
       scheduleManager.deinstall();
+      notificationManager.deinstall();
       pipelineManager.deinstall();
       performDrop(null);
     }
@@ -534,6 +539,27 @@ public class Jobs extends org.apache.man
     analyzeTable();
   }
 
+  /** Find a list of jobs matching specified notification names.
+  */
+  public Long[] findJobsMatchingNotifications(List<String> 
notificationConnectionNames)
+    throws ManifoldCFException
+  {
+    StringBuilder query = new StringBuilder();
+    ArrayList params = new ArrayList();
+    query.append("SELECT ").append(idField)
+      .append(" FROM ").append(getTableName()).append(" t1 WHERE EXISTS(");
+    
notificationManager.buildNotificationQueryClause(query,params,"t1."+idField,notificationConnectionNames);
+    query.append(")");
+    IResultSet set = performQuery(query.toString(),params,null,null);
+    Long[] rval = new Long[set.getRowCount()];
+    for (int i = 0; i < rval.length; i++)
+    {
+      IResultRow row = set.getRow(i);
+      rval[i] = (Long)row.getValue(idField);
+    }
+    return rval;
+  }
+
   /** Find a list of jobs matching specified transformation names.
   */
   public Long[] findJobsMatchingTransformations(List<String> 
transformationConnectionNames)
@@ -780,6 +806,7 @@ public class Jobs extends org.apache.man
         scheduleManager.deleteRows(id);
         hopFilterManager.deleteRows(id);
         pipelineManager.deleteRows(id);
+        notificationManager.deleteRows(id);
         ArrayList params = new ArrayList();
         String query = buildConjunctionClause(params,new ClauseDescription[]{
           new UnitaryClause(idField,id)});
@@ -933,6 +960,14 @@ public class Jobs extends org.apache.man
                     
values.put(assessmentStateField,assessmentStateToString(ASSESSMENT_UNKNOWN));
                 }
 
+                // Changing notifications should never reset seeding.
+                /*
+                if (isSame)
+                {
+                  isSame = notificationManager.compareRows(id,jobDescription);
+                }
+                */
+                
                 if (isSame)
                 {
                   String oldDocSpecXML = 
(String)row.getValue(documentSpecField);
@@ -951,6 +986,7 @@ public class Jobs extends org.apache.man
                   new UnitaryClause(idField,id)});
                 performUpdate(values," WHERE "+query,params,null);
                 pipelineManager.deleteRows(id);
+                notificationManager.deleteRows(id);
                 scheduleManager.deleteRows(id);
                 hopFilterManager.deleteRows(id);
               }
@@ -968,6 +1004,8 @@ public class Jobs extends org.apache.man
 
               // Write pipeline rows
               pipelineManager.writeRows(id,jobDescription);
+              // Write notification rows
+              notificationManager.writeRows(id,jobDescription);
               // Write schedule records
               scheduleManager.writeRows(id,jobDescription);
               // Write hop filter rows
@@ -1476,6 +1514,16 @@ public class Jobs extends org.apache.man
     invalidateCurrentUnregisteredState(jobID,oldStatusValue);
   }
   
+  /** Signal to a job that its underlying notification connector has gone away.
+  *@param jobID is the identifier of the job.
+  *@param oldStatusValue is the current status value for the job.
+  */
+  public void noteNotificationConnectorDeregistration(Long jobID, int 
oldStatusValue)
+    throws ManifoldCFException
+  {
+    // MHL
+  }
+  
   /** Signal to a job that its underlying connector has gone away.
   *@param jobID is the identifier of the job.
   *@param oldStatusValue is the current status value for the job.
@@ -1516,6 +1564,16 @@ public class Jobs extends org.apache.man
     performUpdate(newValues,"WHERE "+query,list,invKey);
   }
   
+  /** Signal to a job that its underlying notification connector has returned.
+  *@param jobID is the identifier of the job.
+  *@param oldStatusValue is the current status value for the job.
+  */
+  public void noteNotificationConnectorRegistration(Long jobID, int 
oldStatusValue)
+    throws ManifoldCFException
+  {
+    // MHL
+  }
+
   /** Signal to a job that its underlying connector has returned.
   *@param jobID is the identifier of the job.
   *@param oldStatusValue is the current status value for the job.
@@ -1542,6 +1600,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
+  {
+    // Notification configuration change does not change anything in a crawl
+  }
+
   /** 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 +3030,21 @@ 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
+  {
+    ArrayList list = new ArrayList();
+    String query = buildConjunctionClause(list,new ClauseDescription[]{
+      new 
UnitaryClause(notificationManager.notificationNameField,connectionName)});
+    IResultSet set = performQuery("SELECT "+notificationManager.ownerIDField+" 
FROM "+notificationManager.getTableName()+
+      " WHERE "+query,list,new StringSet(getJobsKey()),null);
+    return set.getRowCount() > 0;
+  }
+
   /** 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.
@@ -3519,6 +3602,7 @@ public class Jobs extends org.apache.man
 
       // Fill in schedules for jobs
       pipelineManager.getRows(returnValues,idList,params);
+      notificationManager.getRows(returnValues,idList,params);
       scheduleManager.getRows(returnValues,idList,params);
       hopFilterManager.getRows(returnValues,idList,params);
     }

Modified: 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/CrawlerAgent.java
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/CrawlerAgent.java?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/CrawlerAgent.java
 (original)
+++ 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/CrawlerAgent.java
 Tue Dec 23 14:46:43 2014
@@ -784,6 +784,7 @@ public class CrawlerAgent implements IAg
 
     // Threads are down; release connectors
     RepositoryConnectorPoolFactory.make(threadContext).flushUnusedConnectors();
+    
NotificationConnectorPoolFactory.make(threadContext).flushUnusedConnectors();
     numWorkerThreads = 0;
     numDeleteThreads = 0;
     numExpireThreads = 0;

Modified: 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/IdleCleanupThread.java
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/IdleCleanupThread.java?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/IdleCleanupThread.java
 (original)
+++ 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/IdleCleanupThread.java
 Tue Dec 23 14:46:43 2014
@@ -56,6 +56,7 @@ public class IdleCleanupThread extends T
       IThreadContext threadContext = ThreadContextFactory.make();
       
       IRepositoryConnectorPool repositoryConnectorPool = 
RepositoryConnectorPoolFactory.make(threadContext);
+      INotificationConnectorPool notificationConnectorPool = 
NotificationConnectorPoolFactory.make(threadContext);
       
       // Loop
       while (true)
@@ -65,6 +66,7 @@ public class IdleCleanupThread extends T
         {
           // Do the cleanup
           repositoryConnectorPool.pollAllConnectors();
+          notificationConnectorPool.pollAllConnectors();
           // This is unnecessary because agents.interfaces.IdleCleanupThread 
does it.
           //ManifoldCF.pollAll(threadContext);
           

Modified: 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/JobNotificationThread.java
URL: 
http://svn.apache.org/viewvc/manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/JobNotificationThread.java?rev=1647585&r1=1647584&r2=1647585&view=diff
==============================================================================
--- 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/JobNotificationThread.java
 (original)
+++ 
manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/JobNotificationThread.java
 Tue Dec 23 14:46:43 2014
@@ -72,15 +72,14 @@ public class JobNotificationThread exten
           // Before we begin, conditionally reset
           resetManager.waitForReset(threadContext);
 
+          // Find the jobs ready for inactivity and notify them
           JobNotifyRecord[] jobsNeedingNotification = 
jobManager.getJobsReadyForInactivity(processID);
           try
           {
             Set<OutputAndRepositoryConnection> connectionNames = new 
HashSet<OutputAndRepositoryConnection>();
             
-            int k = 0;
-            while (k < jobsNeedingNotification.length)
+            for (JobNotifyRecord jsr : jobsNeedingNotification)
             {
-              JobNotifyRecord jsr = jobsNeedingNotification[k++];
               Long jobID = jsr.getJobID();
               IJobDescription job = jobManager.load(jobID,true);
               if (job != null)
@@ -147,10 +146,8 @@ public class JobNotificationThread exten
             }
             
             // Go through jobs again, and put the notified ones into the 
inactive state.
-            k = 0;
-            while (k < jobsNeedingNotification.length)
+            for (JobNotifyRecord jsr : jobsNeedingNotification)
             {
-              JobNotifyRecord jsr = jobsNeedingNotification[k++];
               Long jobID = jsr.getJobID();
               IJobDescription job = jobManager.load(jobID,true);
               if (job != null)
@@ -249,16 +246,14 @@ public class JobNotificationThread exten
               throw exception;
           }
 
-          // ???
+          // We also need to do a notify for jobs that are about to be deleted
           JobNotifyRecord[] jobsNeedingDeleteNotification = 
jobManager.getJobsReadyForDelete(processID);
           try
           {
             Set<OutputAndRepositoryConnection> connectionNames = new 
HashSet<OutputAndRepositoryConnection>();
             
-            int k = 0;
-            while (k < jobsNeedingDeleteNotification.length)
+            for (JobNotifyRecord jsr : jobsNeedingDeleteNotification)
             {
-              JobNotifyRecord jsr = jobsNeedingDeleteNotification[k++];
               Long jobID = jsr.getJobID();
               IJobDescription job = jobManager.load(jobID,true);
               if (job != null)
@@ -325,10 +320,8 @@ public class JobNotificationThread exten
             }
             
             // Go through jobs again, and put the notified ones into the 
inactive state.
-            k = 0;
-            while (k < jobsNeedingDeleteNotification.length)
+            for (JobNotifyRecord jsr : jobsNeedingDeleteNotification)
             {
-              JobNotifyRecord jsr = jobsNeedingDeleteNotification[k++];
               Long jobID = jsr.getJobID();
               IJobDescription job = jobManager.load(jobID,true);
               if (job != null)


Reply via email to