You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@manifoldcf.apache.org by kw...@apache.org on 2010/07/14 14:55:28 UTC

svn commit: r964035 [1/4] - in /incubator/lcf/trunk/modules: ./ framework/ framework/api/ framework/api/WEB-INF/ framework/api/WEB-INF/lib/ framework/api/org/ framework/api/org/apache/ framework/api/org/apache/lcf/ framework/api/org/apache/lcf/api/ fra...

Author: kwright
Date: Wed Jul 14 12:55:26 2010
New Revision: 964035

URL: http://svn.apache.org/viewvc?rev=964035&view=rev
Log:
Add partial LCF api.  The API's context path is lcf-api.  Current support in place for json versions of all job-related commands.  Additional commands, and documentation too, will follow.

Added:
    incubator/lcf/trunk/modules/framework/api/
    incubator/lcf/trunk/modules/framework/api/WEB-INF/
    incubator/lcf/trunk/modules/framework/api/WEB-INF/lib/
    incubator/lcf/trunk/modules/framework/api/WEB-INF/web.xml   (with props)
    incubator/lcf/trunk/modules/framework/api/org/
    incubator/lcf/trunk/modules/framework/api/org/apache/
    incubator/lcf/trunk/modules/framework/api/org/apache/lcf/
    incubator/lcf/trunk/modules/framework/api/org/apache/lcf/api/
    incubator/lcf/trunk/modules/framework/api/org/apache/lcf/api/APIServlet.java   (with props)
    incubator/lcf/trunk/modules/framework/api/org/apache/lcf/api/Argument.java   (with props)
    incubator/lcf/trunk/modules/json/
    incubator/lcf/trunk/modules/json/org/
    incubator/lcf/trunk/modules/json/org/json/
    incubator/lcf/trunk/modules/json/org/json/CDL.java   (with props)
    incubator/lcf/trunk/modules/json/org/json/Cookie.java   (with props)
    incubator/lcf/trunk/modules/json/org/json/CookieList.java   (with props)
    incubator/lcf/trunk/modules/json/org/json/HTTP.java   (with props)
    incubator/lcf/trunk/modules/json/org/json/HTTPTokener.java   (with props)
    incubator/lcf/trunk/modules/json/org/json/JSONArray.java   (with props)
    incubator/lcf/trunk/modules/json/org/json/JSONException.java   (with props)
    incubator/lcf/trunk/modules/json/org/json/JSONML.java   (with props)
    incubator/lcf/trunk/modules/json/org/json/JSONObject.java   (with props)
    incubator/lcf/trunk/modules/json/org/json/JSONString.java   (with props)
    incubator/lcf/trunk/modules/json/org/json/JSONStringer.java   (with props)
    incubator/lcf/trunk/modules/json/org/json/JSONTokener.java   (with props)
    incubator/lcf/trunk/modules/json/org/json/JSONWriter.java   (with props)
    incubator/lcf/trunk/modules/json/org/json/Test.java   (with props)
    incubator/lcf/trunk/modules/json/org/json/XML.java   (with props)
    incubator/lcf/trunk/modules/json/org/json/XMLTokener.java   (with props)
Modified:
    incubator/lcf/trunk/modules/build.xml
    incubator/lcf/trunk/modules/framework/build.xml
    incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/ConfigNode.java
    incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/Configuration.java
    incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/ConfigurationNode.java
    incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/SpecificationNode.java
    incubator/lcf/trunk/modules/framework/jetty-runner/org/apache/lcf/jettyrunner/LCFJettyRunner.java
    incubator/lcf/trunk/modules/framework/pull-agent/org/apache/lcf/crawler/system/LCF.java

Modified: incubator/lcf/trunk/modules/build.xml
URL: http://svn.apache.org/viewvc/incubator/lcf/trunk/modules/build.xml?rev=964035&r1=964034&r2=964035&view=diff
==============================================================================
--- incubator/lcf/trunk/modules/build.xml (original)
+++ incubator/lcf/trunk/modules/build.xml Wed Jul 14 12:55:26 2010
@@ -38,11 +38,26 @@
         <delete dir="connectors/webcrawler/lib"/>
     </target>
     
-    <target name="build-framework">
+    <target name="build-json">
+        <mkdir dir="build/json/classes"/>
+        <javac srcdir="json" destdir="build/json/classes" target="1.5" source="1.4" debug="true" debuglevel="lines,vars,source">
+            <classpath>
+            </classpath>
+        </javac>
+    </target>
     
+    <target name="jar-json" depends="build-json">
+        <mkdir dir="build/jar"/>
+        <jar destfile="build/jar/json.jar" basedir="build/json/classes"/>
+    </target>
+    
+    <target name="build-framework" depends="jar-json">
         <mkdir dir="framework/lib"/>
         <!-- Individually specify the needed libraries, since otherwise that information would not be available readily -->
         <copy todir="framework/lib">
+            <fileset dir="build/jar">
+                <include name="json.jar"/>
+            </fileset>
             <fileset dir="lib">
                 <include name="commons-codec*.jar"/>
                 <include name="commons-collections*.jar"/>
@@ -656,7 +671,7 @@
         <mkdir dir="build/example"/>
         <manifest file="build/example/manifest">
             <attribute name="Main-Class" value="org.apache.lcf.jettyrunner.LCFJettyRunner"/>
-            <attribute name="Class-Path" value="lib/commons-codec.jar lib/commons-collections.jar lib/commons-el.jar lib/commons-fileupload.jar lib/commons-httpclient-lcf.jar lib/commons-io.jar lib/commons-logging.jar lib/derbyclient.jar lib/derby.jar lib/derbyLocale_cs.jar lib/derbyLocale_de_DE.jar lib/derbyLocale_es.jar lib/derbyLocale_fr.jar lib/derbyLocale_hu.jar lib/derbyLocale_it.jar lib/derbyLocale_ja_JP.jar lib/derbyLocale_ko_KR.jar lib/derbyLocale_pl.jar lib/derbyLocale_pt_BR.jar lib/derbyLocale_ru.jar lib/derbyLocale_zh_CN.jar lib/derbyLocale_zh_TW.jar lib/derbynet.jar lib/derbyrun.jar lib/derbytools.jar lib/eclipse-ecj.jar lib/jasper-6.0.24.jar lib/jasper-el-6.0.24.jar lib/jdbcpool-0.99.jar lib/jetty-6.1.22.jar lib/jetty-util-6.1.22.jar lib/jsp-api-2.1-glassfish-9.1.1.B60.25.p2.jar lib/lcf-agents.jar lib/lcf-core.jar lib/lcf-jetty-runner.jar lib/lcf-pull-agent.jar lib/lcf-ui-core.jar lib/log4j-1.2.jar lib/postgresql.jar lib/serializer.jar lib/servlet-api-2.5-20081
 211.jar lib/tomcat-juli-6.0.24.jar lib/xalan2.jar lib/xercesImpl-lcf.jar lib/xml-apis.jar"/>
+            <attribute name="Class-Path" value="lib/commons-codec.jar lib/commons-collections.jar lib/commons-el.jar lib/commons-fileupload.jar lib/commons-httpclient-lcf.jar lib/commons-io.jar lib/commons-logging.jar lib/derbyclient.jar lib/derby.jar lib/derbyLocale_cs.jar lib/derbyLocale_de_DE.jar lib/derbyLocale_es.jar lib/derbyLocale_fr.jar lib/derbyLocale_hu.jar lib/derbyLocale_it.jar lib/derbyLocale_ja_JP.jar lib/derbyLocale_ko_KR.jar lib/derbyLocale_pl.jar lib/derbyLocale_pt_BR.jar lib/derbyLocale_ru.jar lib/derbyLocale_zh_CN.jar lib/derbyLocale_zh_TW.jar lib/derbynet.jar lib/derbyrun.jar lib/derbytools.jar lib/eclipse-ecj.jar lib/jasper-6.0.24.jar lib/jasper-el-6.0.24.jar lib/jdbcpool-0.99.jar lib/jetty-6.1.22.jar lib/jetty-util-6.1.22.jar lib/jsp-api-2.1-glassfish-9.1.1.B60.25.p2.jar lib/json.jar lib/lcf-agents.jar lib/lcf-core.jar lib/lcf-jetty-runner.jar lib/lcf-pull-agent.jar lib/lcf-ui-core.jar lib/log4j-1.2.jar lib/postgresql.jar lib/serializer.jar lib/servlet-
 api-2.5-20081211.jar lib/tomcat-juli-6.0.24.jar lib/xalan2.jar lib/xercesImpl-lcf.jar lib/xml-apis.jar"/>
         </manifest>
         <jar destfile="dist/example/start.jar" manifest="build/example/manifest"/>
     </target>

Added: incubator/lcf/trunk/modules/framework/api/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/incubator/lcf/trunk/modules/framework/api/WEB-INF/web.xml?rev=964035&view=auto
==============================================================================
--- incubator/lcf/trunk/modules/framework/api/WEB-INF/web.xml (added)
+++ incubator/lcf/trunk/modules/framework/api/WEB-INF/web.xml Wed Jul 14 12:55:26 2010
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<!DOCTYPE web-app
+	PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
+	"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
+
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more
+     contributor license agreements. See the NOTICE file distributed with
+     this work for additional information regarding copyright ownership.
+     The ASF licenses this file to You under the Apache License, Version 2.0
+     (the "License"); you may not use this file except in compliance with
+     the License. You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<web-app>
+  <display-name>Lucene Connectors Framework General API</display-name>
+
+  <description>Lucene Connectors Framework General API</description>
+
+  <servlet>
+	<servlet-name>APIServlet</servlet-name>
+    	<servlet-class>org.apache.lcf.api.APIServlet</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+        <servlet-name>APIServlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+  </servlet-mapping>
+
+  <session-config>
+	<session-timeout>5</session-timeout>
+  </session-config>
+
+</web-app>

Propchange: incubator/lcf/trunk/modules/framework/api/WEB-INF/web.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/lcf/trunk/modules/framework/api/WEB-INF/web.xml
------------------------------------------------------------------------------
    svn:keywords = Id

Added: incubator/lcf/trunk/modules/framework/api/org/apache/lcf/api/APIServlet.java
URL: http://svn.apache.org/viewvc/incubator/lcf/trunk/modules/framework/api/org/apache/lcf/api/APIServlet.java?rev=964035&view=auto
==============================================================================
--- incubator/lcf/trunk/modules/framework/api/org/apache/lcf/api/APIServlet.java (added)
+++ incubator/lcf/trunk/modules/framework/api/org/apache/lcf/api/APIServlet.java Wed Jul 14 12:55:26 2010
@@ -0,0 +1,177 @@
+/* $Id$ */
+
+/**
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.apache.lcf.api;
+
+import org.apache.lcf.core.interfaces.*;
+import org.apache.lcf.agents.interfaces.*;
+import org.apache.lcf.crawler.interfaces.*;
+import org.apache.lcf.crawler.system.LCF;
+import org.apache.lcf.crawler.system.Logging;
+
+import java.io.*;
+import java.util.*;
+import java.net.*;
+
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/** This servlet class provides API services for LCF.
+*/
+public class APIServlet extends HttpServlet
+{
+  public static final String _rcsid = "@(#)$Id$";
+
+  /** The init method.
+  */
+  public void init(ServletConfig config)
+    throws ServletException
+  {
+    super.init(config);
+    try
+    {
+      // Set up the environment
+      LCF.initializeEnvironment();
+      // Nothing more needs to be done at this point.
+    }
+    catch (LCFException e)
+    {
+      Logging.misc.error("Error starting API service: "+e.getMessage(),e);
+      throw new ServletException("Error starting API service: "+e.getMessage(),e);
+    }
+
+  }
+
+  /** The destroy method.
+  */
+  public void destroy()
+  {
+    try
+    {
+      // Set up the environment
+      LCF.initializeEnvironment();
+      // Nothing more needs to be done.
+    }
+    catch (LCFException e)
+    {
+      Logging.misc.error("Error shutting down API service: "+e.getMessage(),e);
+    }
+    super.destroy();
+  }
+
+  /** The get method.
+  */
+  protected void doGet(HttpServletRequest request, HttpServletResponse response)
+    throws ServletException, IOException
+  {
+    try
+    {
+      // Set up the environment
+      LCF.initializeEnvironment();
+
+      // Mint a thread context
+      IThreadContext tc = ThreadContextFactory.make();
+      
+      // Get the path info string.  This will furnish the command.
+      String pathInfo = request.getPathInfo();
+      
+      // The first field of the pathInfo string is the protocol.  Someday this will be a dispatcher using reflection.  Right now, we only support json, so a quick check is fine.
+      if (pathInfo == null)
+      {
+        response.sendError(response.SC_BAD_REQUEST,"Unknown API protocol");
+        return;
+      }
+      
+      // Strip off leading "/"
+      if (pathInfo.startsWith("/"))
+        pathInfo = pathInfo.substring(1);
+      
+      int index = pathInfo.indexOf("/");
+      String protocol;
+      String command;
+      if (index == -1)
+      {
+        protocol = pathInfo;
+        command = "";
+      }
+      else
+      {
+        protocol = pathInfo.substring(0,index);
+        command = pathInfo.substring(index+1);
+      }
+        
+      // Handle multipart forms
+      IPostParameters parameters = new org.apache.lcf.ui.multipart.MultipartWrapper(request);
+        
+      // Input
+      String argument = parameters.getParameter("object");
+      // Output
+      String outputText = null;
+
+      if (protocol.equals("json"))
+      {
+        // Parse the input argument, if it is present
+        Configuration input;
+        if (argument != null)
+        {
+          input = new Argument();
+          input.fromJSON(argument);
+        }
+        else
+          input = null;
+          
+        Configuration output = LCF.executeCommand(tc,command,input);
+          
+        // Format the response
+        outputText = output.toJSON();
+      }
+      else
+      {
+        response.sendError(response.SC_BAD_REQUEST,"Unknown API protocol: "+protocol);
+        return;
+      }
+        
+      byte[] responseValue = outputText.getBytes("utf-8");
+
+      // Set response mime type
+      response.setContentType("text/plain; charset=utf-8");
+      response.setIntHeader("Content-Length", (int)responseValue.length);
+      ServletOutputStream out = response.getOutputStream();
+      try
+      {
+        out.write(responseValue,0,responseValue.length);
+        out.flush();
+      }
+      finally
+      {
+        out.close();
+      }
+    }
+    catch (java.io.UnsupportedEncodingException e)
+    {
+      //Logging.authorityService.error("Unsupported encoding: "+e.getMessage(),e);
+      throw new ServletException("Fatal error occurred: "+e.getMessage(),e);
+    }
+    catch (LCFException e)
+    {
+      //Logging.authorityService.error("API servlet error: "+e.getMessage(),e);
+      response.sendError(response.SC_INTERNAL_SERVER_ERROR,e.getMessage());
+    }
+  }
+
+}

Propchange: incubator/lcf/trunk/modules/framework/api/org/apache/lcf/api/APIServlet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/lcf/trunk/modules/framework/api/org/apache/lcf/api/APIServlet.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: incubator/lcf/trunk/modules/framework/api/org/apache/lcf/api/Argument.java
URL: http://svn.apache.org/viewvc/incubator/lcf/trunk/modules/framework/api/org/apache/lcf/api/Argument.java?rev=964035&view=auto
==============================================================================
--- incubator/lcf/trunk/modules/framework/api/org/apache/lcf/api/Argument.java (added)
+++ incubator/lcf/trunk/modules/framework/api/org/apache/lcf/api/Argument.java Wed Jul 14 12:55:26 2010
@@ -0,0 +1,54 @@
+/* $Id$ */
+
+/**
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.apache.lcf.api;
+
+import org.apache.lcf.core.interfaces.*;
+import java.util.*;
+import java.io.*;
+
+/** This class represents a specification, which is a generalized hierarchy of nodes that
+* can be interpreted by an appropriate connector in an appropriate way.
+*/
+public class Argument extends Configuration
+{
+  public static final String _rcsid = "@(#)$Id$";
+
+  /** Constructor.
+  */
+  public Argument()
+  {
+    super();
+  }
+
+  /** Return the root node type.
+  *@return the node type name.
+  */
+  protected String getRootNodeLabel()
+  {
+    return "object";
+  }
+  
+  /** Create a new object of the appropriate class.
+  */
+  protected Configuration createNew()
+  {
+    return new Argument();
+  }
+  
+}

Propchange: incubator/lcf/trunk/modules/framework/api/org/apache/lcf/api/Argument.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/lcf/trunk/modules/framework/api/org/apache/lcf/api/Argument.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: incubator/lcf/trunk/modules/framework/build.xml
URL: http://svn.apache.org/viewvc/incubator/lcf/trunk/modules/framework/build.xml?rev=964035&r1=964034&r2=964035&view=diff
==============================================================================
--- incubator/lcf/trunk/modules/framework/build.xml (original)
+++ incubator/lcf/trunk/modules/framework/build.xml Wed Jul 14 12:55:26 2010
@@ -98,6 +98,20 @@
         </javac>
     </target>
 
+    <target name="compile-api" depends="compile-core,compile-ui-core,compile-agents,compile-pull-agent">
+        <mkdir dir="build/api/classes"/>
+        <javac srcdir="api" destdir="build/api/classes" target="1.5" source="1.4" debug="true" debuglevel="lines,vars,source">
+            <classpath>
+                 <fileset dir="lib"> 
+                    <include name="*.jar"/> 
+                </fileset>
+                <pathelement location="build/core/classes"/>
+                <pathelement location="build/ui-core/classes"/>
+                <pathelement location="build/agents/classes"/>
+                <pathelement location="build/pull-agent/classes"/>
+            </classpath>
+        </javac>
+    </target>
 
     <target name="compile-crawler-ui" depends="compile-core,compile-ui-core,compile-agents,compile-pull-agent">
         <!-- Define the jsp compilation task using tomcat libraries -->
@@ -173,6 +187,7 @@
                 <include name="commons-io*.jar"/>
                 <include name="commons-logging*.jar"/>
                 <include name="jdbcpool-0.99.jar"/>
+                <include name="json.jar"/>
                 <include name="log4j*.jar"/>
                 <include name="serializer*.jar"/>
                 <include name="xalan*.jar"/>
@@ -193,7 +208,42 @@
         </copy>
         <jar destfile="build/webapp/authority-service/WEB-INF/lib/lcf-authority-servlet.jar" basedir="build/authority-service/classes"/>
     </target>
-    
+
+    <target name="webapp-api" depends="jar-core,jar-ui-core,jar-agents,jar-pull-agent,compile-api">
+        <mkdir dir="build/webapp/api/WEB-INF/lib"/>
+        <copy todir="build/webapp/api/WEB-INF/lib">
+            <fileset dir="lib">
+                <include name="commons-codec*.jar"/>
+                <include name="commons-collections*.jar"/>
+                <include name="commons-el*.jar"/>
+                <include name="commons-fileupload*.jar"/>
+                <include name="commons-httpclient-lcf.jar"/>
+                <include name="commons-io*.jar"/>
+                <include name="commons-logging*.jar"/>
+                <include name="jdbcpool-0.99.jar"/>
+                <include name="json.jar"/>
+                <include name="log4j*.jar"/>
+                <include name="serializer*.jar"/>
+                <include name="xalan*.jar"/>
+                <include name="xercesImpl-lcf.jar"/>
+                <include name="xml-apis*.jar"/>
+            </fileset>
+            <fileset dir="lib">
+                <include name="postgresql*.jar"/>
+                <include name="derby*.jar"/>
+            </fileset>
+        </copy>
+        <copy todir="build/webapp/api/WEB-INF/lib">
+            <fileset dir="build/jar">
+                <include name="lcf-core.jar"/>
+                <include name="lcf-ui-core.jar"/>
+                <include name="lcf-agents.jar"/>
+                <include name="lcf-pull-agent.jar"/>
+            </fileset>
+        </copy>
+        <jar destfile="build/webapp/api/WEB-INF/lib/lcf-api-servlet.jar" basedir="build/api/classes"/>
+    </target>
+
     <target name="webapp-crawler-ui" depends="compile-crawler-ui,jar-core,jar-ui-core,jar-agents,jar-pull-agent">
         <mkdir dir="build/webapp/crawler-ui/WEB-INF/lib"/>
         <copy todir="build/webapp/crawler-ui/WEB-INF/lib">
@@ -211,6 +261,7 @@
                 <include name="commons-io*.jar"/>
                 <include name="commons-logging*.jar"/>
                 <include name="jdbcpool-0.99.jar"/>
+                <include name="json.jar"/>
                 <include name="log4j*.jar"/>
                 <include name="serializer*.jar"/>
                 <include name="xalan*.jar"/>
@@ -245,7 +296,12 @@
         <mkdir dir="dist/web/war"/>
         <war destfile="dist/web/war/lcf-authority-service.war" webxml="authority-service/WEB-INF/web.xml" basedir="build/webapp/authority-service"/>
     </target>
-    
+
+    <target name="war-api" depends="webapp-api">
+        <mkdir dir="dist/web/war"/>
+        <war destfile="dist/web/war/lcf-api.war" webxml="api/WEB-INF/web.xml" basedir="build/webapp/api"/>
+    </target>
+
     <target name="war-crawler-ui" depends="webapp-crawler-ui">
         <mkdir dir="dist/web/war"/>
         <war destfile="dist/web/war/lcf-crawler-ui.war" webxml="crawler-ui/WEB-INF/web.xml" basedir="build/webapp/crawler-ui"/>
@@ -263,6 +319,7 @@
                 <include name="commons-io*.jar"/>
                 <include name="commons-logging*.jar"/>
                 <include name="jdbcpool-0.99.jar"/>
+                <include name="json.jar"/>
                 <include name="log4j*.jar"/>
                 <include name="serializer*.jar"/>
                 <include name="servlet-api*.jar"/>
@@ -288,7 +345,7 @@
         </copy>
     </target>
 
-    <target name="example" depends="war-crawler-ui,war-authority-service,jar-jetty-runner,jar-core,jar-agents,jar-pull-agent">
+    <target name="example" depends="war-crawler-ui,war-api,war-authority-service,jar-jetty-runner,jar-core,jar-agents,jar-pull-agent">
         <mkdir dir="dist/example/lib"/>
         <copy todir="dist/example/lib">
             <fileset dir="lib">
@@ -303,6 +360,7 @@
                 <include name="jasper*.jar"/>
                 <include name="jdbcpool-0.99.jar"/>
                 <include name="jetty*.jar"/>
+                <include name="json.jar"/>
                 <include name="jsp-api*.jar"/>
                 <include name="jsp-2.5*.jar"/>
                 <include name="log4j*.jar"/>
@@ -342,7 +400,7 @@
         </copy>
     </target>
     
-    <target name="compile-tests" depends="compile-core,compile-ui-core,compile-agents,compile-pull-agent,compile-authority-service">
+    <target name="compile-tests" depends="compile-core,compile-ui-core,compile-agents,compile-pull-agent,compile-authority-service,compile-api">
         <mkdir dir="build/tests/classes"/>
         <javac srcdir="tests" destdir="build/tests/classes" target="1.5" source="1.5" debug="true" debuglevel="lines,vars,source">
             <classpath>
@@ -354,6 +412,7 @@
                 <pathelement location="build/agents/classes"/>
                 <pathelement location="build/pull-agent/classes"/>
                 <pathelement location="build/authority-service/classes"/>
+                <pathelement location="build/api/classes"/>
             </classpath>
         </javac>
     </target>
@@ -384,6 +443,7 @@
                 <pathelement location="build/agents/classes"/>
                 <pathelement location="build/pull-agent/classes"/>
                 <pathelement location="build/authority-service/classes"/>
+                <pathelement location="build/api/classes"/>
                 <pathelement location="build/tests/classes"/>
             </classpath>
             <test name="org.apache.lcf.agents.tests.Sanity" todir="test-output"/>
@@ -392,6 +452,6 @@
         </junit>
     </target>
     
-    <target name="all" depends="processes,war-authority-service,war-crawler-ui,example,doc,tests,run-tests"/>
+    <target name="all" depends="processes,war-authority-service,war-crawler-ui,war-api,example,doc,tests,run-tests"/>
     
 </project>

Modified: incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/ConfigNode.java
URL: http://svn.apache.org/viewvc/incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/ConfigNode.java?rev=964035&r1=964034&r2=964035&view=diff
==============================================================================
--- incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/ConfigNode.java (original)
+++ incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/ConfigNode.java Wed Jul 14 12:55:26 2010
@@ -35,12 +35,26 @@ public class ConfigNode extends Configur
     super(type);
   }
 
+  /** Copy constructor.
+  */
+  public ConfigNode(ConfigurationNode source)
+  {
+    super(source);
+  }
+  
   /** Create a new node of this same type and class.
   */
   protected ConfigurationNode createNewNode()
   {
     return new ConfigNode(type);
   }
+
+  /** Make a new node that is a copy of the specified node.
+  */
+  protected ConfigurationNode createNewNode(ConfigurationNode source)
+  {
+    return new ConfigNode(source);
+  }
   
   /** Duplicate.
   *@return the duplicate.

Modified: incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/Configuration.java
URL: http://svn.apache.org/viewvc/incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/Configuration.java?rev=964035&r1=964034&r2=964035&view=diff
==============================================================================
--- incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/Configuration.java (original)
+++ incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/Configuration.java Wed Jul 14 12:55:26 2010
@@ -23,6 +23,7 @@ import java.util.*;
 import java.io.*;
 import org.apache.lcf.core.system.LCF;
 import org.apache.lcf.core.common.XMLDoc;
+import org.json.*;
 
 /** This class represents XML configuration information, in its most basic incarnation.
 */
@@ -30,6 +31,12 @@ public abstract class Configuration
 {
   public static final String _rcsid = "@(#)$Id$";
 
+  // JSON special key values
+  
+  protected static final String JSON_ATTRIBUTE = "_attribute_";
+  protected static final String JSON_VALUE = "_value_";
+  
+
   // The children
   protected ArrayList children = new ArrayList();
   // Read-only flag
@@ -139,7 +146,7 @@ public abstract class Configuration
   }
 
   /** Get as XML
-  *@return the xml corresponding to these ConfigParams.
+  *@return the xml corresponding to these Configuration.
   */
   public String toXML()
     throws LCFException
@@ -158,6 +165,35 @@ public abstract class Configuration
     return doc.getXML();
   }
 
+  /** Get as JSON.
+  *@return the json corresponding to this Configuration.
+  */
+  public String toJSON()
+    throws LCFException
+  {
+    try
+    {
+      JSONWriter writer = new JSONStringer();
+      writer.object();
+      // We do NOT use the root node label, unlike XML.
+      // Now, go through all children
+      int i = 0;
+      while (i < children.size())
+      {
+        ConfigurationNode node = (ConfigurationNode)children.get(i++);
+        writeNode(writer,node,true);
+      }
+      writer.endObject();
+      
+      // Convert to a string.
+      return writer.toString();
+    }
+    catch (JSONException e)
+    {
+      throw new LCFException(e.getMessage(),e);
+    }
+  }
+  
   /** Write a specification node.
   *@param doc is the document.
   *@param parent is the parent.
@@ -190,6 +226,103 @@ public abstract class Configuration
     }
   }
 
+  
+  /** Write a JSON specification node.
+  *@param writer is the JSON writer.
+  *@param node is the node.
+  *@param writeKey is true if the key needs to be written, false otherwise.
+  */
+  protected static void writeNode(JSONWriter writer, ConfigurationNode node, boolean writeKey)
+    throws LCFException
+  {
+    try
+    {
+      // Node types correspond directly to keys.  Attributes correspond to "_attribute_<attribute_name>".
+      // Get the type
+      if (writeKey)
+      {
+        String type = node.getType();
+        writer.key(type);
+      }
+      // Problem: Two ways of handling a naked 'value'.  First way is to NOT presume a nested object is needed.  Second way is to require a nested
+      // object.  On reconstruction, the right thing will happen, and a naked value will become a node with a value, while an object will become
+      // a node that has an optional "_value_" key inside it.
+      String value = node.getValue();
+      if (value != null && node.getAttributeCount() == 0 && node.getChildCount() == 0)
+      {
+        writer.value(value);
+      }
+      else
+      {
+        writer.object();
+        
+        if (value != null)
+        {
+          writer.key(JSON_VALUE);
+          writer.value(value);
+        }
+        
+        Iterator iter = node.getAttributes();
+        while (iter.hasNext())
+        {
+          String attribute = (String)iter.next();
+          String attrValue = node.getAttributeValue(attribute);
+          writer.key(JSON_ATTRIBUTE+attribute);
+          writer.value(attrValue);
+        }
+
+        // Now, do children.  To get the arrays right, we need to glue together all children with the
+        // same type, which requires us to do an appropriate pass to gather that stuff together.
+        Map childMap = new HashMap();
+        int i = 0;
+        while (i < node.getChildCount())
+        {
+          ConfigurationNode child = node.findChild(i++);
+          String key = child.getType();
+          ArrayList list = (ArrayList)childMap.get(key);
+          if (list == null)
+          {
+            list = new ArrayList();
+            childMap.put(key,list);
+          }
+          list.add(child);
+        }
+        
+        iter = childMap.keySet().iterator();
+        while (iter.hasNext())
+        {
+          String key = (String)iter.next();
+          ArrayList list = (ArrayList)childMap.get(key);
+          if (list.size() > 1)
+          {
+            // Write the key
+            writer.key(key);
+            // Write it as an array
+            writer.array();
+            i = 0;
+            while (i < list.size())
+            {
+              ConfigurationNode child = (ConfigurationNode)list.get(i++);
+              writeNode(writer,child,false);
+            }
+            writer.endArray();
+          }
+          else
+          {
+            // Write it as a singleton
+            writeNode(writer,(ConfigurationNode)list.get(0),true);
+          }
+        }
+
+        writer.endObject();
+      }
+    }
+    catch (JSONException e)
+    {
+      throw new LCFException(e.getMessage(),e);
+    }
+  }
+  
   /** Read from XML.
   *@param xml is the input XML.
   */
@@ -200,6 +333,134 @@ public abstract class Configuration
     initializeFromDoc(doc);
   }
   
+  /** Read from JSON.
+  *@param json is the input JSON.
+  */
+  public void fromJSON(String json)
+    throws LCFException
+  {
+    if (readOnly)
+      throw new IllegalStateException("Attempt to change read-only object");
+
+    clearChildren();
+    try
+    {
+      JSONObject object = new JSONObject(json);
+      // Convert the object into our configuration
+      Iterator iter = object.keys();
+      while (iter.hasNext())
+      {
+        String key = (String)iter.next();
+        Object x = object.opt(key);
+        if (x instanceof JSONArray)
+        {
+          // Iterate through.
+          JSONArray array = (JSONArray)x;
+          int i = 0;
+          while (i < array.length())
+          {
+            x = array.opt(i++);
+            processObject(key,x);
+          }
+        }
+        else
+          processObject(key,x);
+      }
+    }
+    catch (JSONException e)
+    {
+      throw new LCFException("Json syntax error - "+e.getMessage(),e);
+    }
+  }
+  
+  /** Process a JSON object */
+  protected void processObject(String key, Object x)
+  {
+    if (x instanceof JSONObject)
+    {
+      // Nested single object
+      ConfigurationNode cn = readNode(key,(JSONObject)x);
+      addChild(getChildCount(),cn);
+    }
+    else if (x == JSONObject.NULL)
+    {
+      // Null object.  Don't enter the key.
+    }
+    else
+    {
+      // It's a string or a number or some scalar value
+      String value = x.toString();
+      ConfigurationNode cn = createNewNode(key);
+      cn.setValue(value);
+      addChild(getChildCount(),cn);
+    }
+  }
+  
+  /** Read a node from a json object */
+  protected ConfigurationNode readNode(String key, JSONObject object)
+  {
+    ConfigurationNode rval = createNewNode(key);
+    Iterator iter = object.keys();
+    while (iter.hasNext())
+    {
+      String nestedKey = (String)iter.next();
+      Object x = object.opt(nestedKey);
+      if (x instanceof JSONArray)
+      {
+        // Iterate through.
+        JSONArray array = (JSONArray)x;
+        int i = 0;
+        while (i < array.length())
+        {
+          x = array.opt(i++);
+          processObject(rval,nestedKey,x);
+        }
+      }
+      else
+        processObject(rval,nestedKey,x);
+    }
+    return rval;
+  }
+  
+  /** Process a JSON object */
+  protected void processObject(ConfigurationNode cn, String key, Object x)
+  {
+    if (x instanceof JSONObject)
+    {
+      // Nested single object
+      ConfigurationNode nestedCn = readNode(key,(JSONObject)x);
+      cn.addChild(cn.getChildCount(),nestedCn);
+    }
+    else if (x == JSONObject.NULL)
+    {
+      // Null object.  Don't enter the key.
+    }
+    else
+    {
+      // It's a string or a number or some scalar value
+      String value = x.toString();
+      // Is it an attribute, or a value?
+      if (key.startsWith(JSON_ATTRIBUTE))
+      {
+        // Attribute.  Set the attribute in the current node.
+        cn.setAttribute(key.substring(JSON_ATTRIBUTE.length()),value);
+      }
+      else if (key.equals(JSON_VALUE))
+      {
+        // Value.  Set the value in the current node.
+        cn.setValue(value);
+      }
+      else
+      {
+        // Something we don't recognize, which can only be a simplified key/value pair.
+        // Create a child node representing the key/value pair.
+        ConfigurationNode nestedCn = createNewNode(key);
+        nestedCn.setValue(value);
+        cn.addChild(cn.getChildCount(),nestedCn);
+      }
+    }
+  }
+
   /** Read from an XML binary stream.
   *@param xmlstream is the input XML stream.  Does NOT close the stream.
   */

Modified: incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/ConfigurationNode.java
URL: http://svn.apache.org/viewvc/incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/ConfigurationNode.java?rev=964035&r1=964034&r2=964035&view=diff
==============================================================================
--- incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/ConfigurationNode.java (original)
+++ incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/ConfigurationNode.java Wed Jul 14 12:55:26 2010
@@ -43,6 +43,28 @@ public class ConfigurationNode
     this.type = type;
   }
 
+  /** Duplication constructor.
+  */
+  public ConfigurationNode(ConfigurationNode source)
+  {
+    this.type = source.type;
+    this.value = source.value;
+    this.readOnly = source.readOnly;
+    Iterator iter = source.attributes.keySet().iterator();
+    while (iter.hasNext())
+    {
+      String attribute = (String)iter.next();
+      String attrValue = (String)source.attributes.get(attribute);
+      this.attributes.put(attribute,attrValue);
+    }
+    int i = 0;
+    while (i < source.getChildCount())
+    {
+      ConfigurationNode child = source.findChild(i++);
+      this.addChild(this.getChildCount(),createNewNode(child));
+    }
+  }
+  
   /** Make a new blank node identical in type and class to the current node.
   *@return the new node.
   */
@@ -50,7 +72,14 @@ public class ConfigurationNode
   {
     return new ConfigurationNode(type);
   }
-    
+  
+  /** Make a new node that is a copy of the specified node.
+  */
+  protected ConfigurationNode createNewNode(ConfigurationNode source)
+  {
+    return new ConfigurationNode(source);
+  }
+  
   /** Make this node (and its children) read-only
   */
   public void makeReadOnly()
@@ -109,6 +138,8 @@ public class ConfigurationNode
   {
     if (readOnly)
       throw new IllegalStateException("Attempt to change read-only object");
+    if (value != null && value.length() == 0)
+      value = null;
     this.value = value;
   }
 
@@ -144,7 +175,14 @@ public class ConfigurationNode
   */
   public void removeChild(int index)
   {
-    children.remove(index);
+    if (readOnly)
+      throw new IllegalStateException("Attempt to change read-only object");
+    if (children != null)
+    {
+      children.remove(index);
+      if (children.size() == 0)
+        children = null;
+    }
   }
 
   /** Add child at specified position.
@@ -153,6 +191,9 @@ public class ConfigurationNode
   */
   public void addChild(int index, ConfigurationNode child)
   {
+    if (readOnly)
+      throw new IllegalStateException("Attempt to change read-only object");
+
     if (children == null)
       children = new ArrayList();
     children.add(index,child);
@@ -169,7 +210,11 @@ public class ConfigurationNode
     if (value == null)
     {
       if (attributes != null)
+      {
         attributes.remove(attribute);
+        if (attributes.size() == 0)
+          attributes = null;
+      }
     }
     else
     {
@@ -179,6 +224,17 @@ public class ConfigurationNode
     }
   }
 
+  /** Get the attribute count.
+  *@return the attribute count.
+  */
+  public int getAttributeCount()
+  {
+    if (attributes == null)
+      return 0;
+    else
+      return attributes.size();
+  }
+  
   /** Iterate over attributes.
   *@return the attribute iterator.
   */

Modified: incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/SpecificationNode.java
URL: http://svn.apache.org/viewvc/incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/SpecificationNode.java?rev=964035&r1=964034&r2=964035&view=diff
==============================================================================
--- incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/SpecificationNode.java (original)
+++ incubator/lcf/trunk/modules/framework/core/org/apache/lcf/core/interfaces/SpecificationNode.java Wed Jul 14 12:55:26 2010
@@ -35,12 +35,26 @@ public class SpecificationNode extends C
     super(type);
   }
 
+  /** Copy constructor.
+  */
+  public SpecificationNode(ConfigurationNode source)
+  {
+    super(source);
+  }
+  
   /** Create a new node of this same type and class.
   */
   protected ConfigurationNode createNewNode()
   {
     return new SpecificationNode(type);
   }
+
+  /** Make a new node that is a copy of the specified node.
+  */
+  protected ConfigurationNode createNewNode(ConfigurationNode source)
+  {
+    return new SpecificationNode(source);
+  }
   
   /** Duplicate.
   *@return the duplicate.

Modified: incubator/lcf/trunk/modules/framework/jetty-runner/org/apache/lcf/jettyrunner/LCFJettyRunner.java
URL: http://svn.apache.org/viewvc/incubator/lcf/trunk/modules/framework/jetty-runner/org/apache/lcf/jettyrunner/LCFJettyRunner.java?rev=964035&r1=964034&r2=964035&view=diff
==============================================================================
--- incubator/lcf/trunk/modules/framework/jetty-runner/org/apache/lcf/jettyrunner/LCFJettyRunner.java (original)
+++ incubator/lcf/trunk/modules/framework/jetty-runner/org/apache/lcf/jettyrunner/LCFJettyRunner.java Wed Jul 14 12:55:26 2010
@@ -63,7 +63,7 @@ public class LCFJettyRunner
   
   protected Server server;
   
-  public LCFJettyRunner( int port, String crawlerWarPath, String authorityServiceWarPath )
+  public LCFJettyRunner( int port, String crawlerWarPath, String authorityServiceWarPath, String apiWarPath )
   {
     server = new Server( port );    
     server.setStopAtShutdown( true );
@@ -77,6 +77,9 @@ public class LCFJettyRunner
     // This will cause jetty to ignore all of the framework and jdbc jars in the war, which is what we want.
     lcfAuthorityService.setParentLoaderPriority(true);
     server.addHandler(lcfAuthorityService);
+    WebAppContext lcfApi = new WebAppContext(apiWarPath,"/lcf-api");
+    lcfApi.setParentLoaderPriority(true);
+    server.addHandler(lcfApi);
   }
 
   public void start()
@@ -138,9 +141,9 @@ public class LCFJettyRunner
    */
   public static void main( String[] args )
   {
-    if (args.length != 3 && args.length != 1 && args.length != 0)
+    if (args.length != 4 && args.length != 1 && args.length != 0)
     {
-      System.err.println("Usage: LCFJettyRunner [<port> [<crawler-war-path> <authority-service-war-path>]]");
+      System.err.println("Usage: LCFJettyRunner [<port> [<crawler-war-path> <authority-service-war-path> <api-war-path>]]");
       System.exit(1);
     }
 
@@ -160,10 +163,12 @@ public class LCFJettyRunner
     
     String crawlerWarPath = "war/lcf-crawler-ui.war";
     String authorityserviceWarPath = "war/lcf-authority-service.war";
-    if (args.length == 3)
+    String apiWarPath = "war/lcf-api.war";
+    if (args.length == 4)
     {
       crawlerWarPath = args[1];
       authorityserviceWarPath = args[2];
+      apiWarPath = args[3];
     }
     
     // Ready to begin in earnest...
@@ -404,7 +409,7 @@ public class LCFJettyRunner
       System.err.println("Starting jetty...");
       
       // Create a jetty instance
-      LCFJettyRunner jetty = new LCFJettyRunner(jettyPort,crawlerWarPath,authorityserviceWarPath);
+      LCFJettyRunner jetty = new LCFJettyRunner(jettyPort,crawlerWarPath,authorityserviceWarPath,apiWarPath);
       // This will register a shutdown hook as well.
       jetty.start();
 

Modified: incubator/lcf/trunk/modules/framework/pull-agent/org/apache/lcf/crawler/system/LCF.java
URL: http://svn.apache.org/viewvc/incubator/lcf/trunk/modules/framework/pull-agent/org/apache/lcf/crawler/system/LCF.java?rev=964035&r1=964034&r2=964035&view=diff
==============================================================================
--- incubator/lcf/trunk/modules/framework/pull-agent/org/apache/lcf/crawler/system/LCF.java (original)
+++ incubator/lcf/trunk/modules/framework/pull-agent/org/apache/lcf/crawler/system/LCF.java Wed Jul 14 12:55:26 2010
@@ -983,5 +983,735 @@ public class LCF extends org.apache.lcf.
   {
     return outputActivityName+" ("+outputConnectionName+")";
   }
+  
+  // API support
+  
+  protected static final String API_ERRORNODE = "error";
+  protected static final String API_JOBNODE = "job";
+  protected static final String API_JOBIDNODE = "job_id";
+  
+  /** Execute specified command.  Note that the command is a string, and that it is permitted to accept at most one argument, which
+  * will be a Configuration object, and return the same.
+  *@param tc is the thread context.
+  *@param command is the command.
+  *@param inputArgument is the (optional) argument.
+  *@return the response, which cannot be null.
+  */
+  public static Configuration executeCommand(IThreadContext tc, String command, Configuration inputArgument)
+    throws LCFException
+  {
+    Configuration rval = new ResponseValue();
+    if (command.equals("job/list"))
+    {
+      IJobManager jobManager = JobManagerFactory.make(tc);
+      IJobDescription[] jobs = jobManager.getAllJobs();
+      int i = 0;
+      while (i < jobs.length)
+      {
+        ConfigurationNode job = formatJobDescription(API_JOBNODE,jobs[i++]);
+        rval.addChild(rval.getChildCount(),job);
+      }
+    }
+    else if (command.equals("job/get"))
+    {
+      // Get the job id from the argument
+      if (inputArgument == null)
+      {
+        ConfigurationNode error = new ConfigurationNode(API_ERRORNODE);
+        error.setValue("Input argument required");
+        rval.addChild(rval.getChildCount(),error);
+        return rval;
+      }
+      
+      String jobID = getRootArgument(inputArgument,API_JOBIDNODE);
+      if (jobID == null)
+      {
+        ConfigurationNode error = new ConfigurationNode(API_ERRORNODE);
+        error.setValue("Input argument must have '"+API_JOBIDNODE+"' field");
+        rval.addChild(rval.getChildCount(),error);
+        return rval;
+      }
+      
+      IJobManager jobManager = JobManagerFactory.make(tc);
+      IJobDescription job = jobManager.load(new Long(jobID));
+      if (job != null)
+      {
+        // Fill the return object with job information
+        ConfigurationNode jobNode = formatJobDescription(API_JOBNODE,job);
+        rval.addChild(rval.getChildCount(),jobNode);
+      }
+    }
+    else if (command.equals("job/save"))
+    {
+      // Get the job from the argument
+      if (inputArgument == null)
+      {
+        ConfigurationNode error = new ConfigurationNode(API_ERRORNODE);
+        error.setValue("Input argument required");
+        rval.addChild(rval.getChildCount(),error);
+        return rval;
+      }
+
+      ConfigurationNode jobNode = findConfigurationNode(inputArgument,API_JOBNODE);
+      if (jobNode == null)
+      {
+        ConfigurationNode error = new ConfigurationNode(API_ERRORNODE);
+        error.setValue("Input argument must have '"+API_JOBNODE+"' field");
+        rval.addChild(rval.getChildCount(),error);
+        return rval;
+      }
+      
+      // Turn the configuration node into a JobDescription
+      IJobDescription job = readJobDescription(tc,jobNode);
+      
+      // Save the job.
+      IJobManager jobManager = JobManagerFactory.make(tc);
+      jobManager.save(job);
+    }
+    else if (command.equals("job/delete"))
+    {
+      // Get the job id from the argument
+      if (inputArgument == null)
+      {
+        ConfigurationNode error = new ConfigurationNode(API_ERRORNODE);
+        error.setValue("Input argument required");
+        rval.addChild(rval.getChildCount(),error);
+        return rval;
+      }
+      
+      String jobID = getRootArgument(inputArgument,API_JOBIDNODE);
+      if (jobID == null)
+      {
+        ConfigurationNode error = new ConfigurationNode(API_ERRORNODE);
+        error.setValue("Input argument must have '"+API_JOBIDNODE+"' field");
+        rval.addChild(rval.getChildCount(),error);
+        return rval;
+      }
+
+      IJobManager jobManager = JobManagerFactory.make(tc);
+      jobManager.deleteJob(new Long(jobID));
+    }
+    else if (command.equals("jobstatus/list"))
+    {
+    }
+    else if (command.equals("jobstatus/start"))
+    {
+    }
+    else if (command.equals("jobstatus/abort"))
+    {
+    }
+    else if (command.equals("jobstatus/restart"))
+    {
+    }
+    else if (command.equals("jobstatus/pause"))
+    {
+    }
+    else if (command.equals("jobstatus/resume"))
+    {
+    }
+    else if (command.equals("outputconnection/list"))
+    {
+    }
+    else if (command.equals("outputconnection/get"))
+    {
+    }
+    else if (command.equals("outputconnection/save"))
+    {
+    }
+    else if (command.equals("outputconnection/delete"))
+    {
+    }
+    else if (command.equals("outputconnection/checkstatus"))
+    {
+    }
+    else if (command.equals("repositoryconnection/list"))
+    {
+    }
+    else if (command.equals("repositoryconnection/get"))
+    {
+    }
+    else if (command.equals("repositoryconnection/save"))
+    {
+    }
+    else if (command.equals("repositoryconnection/delete"))
+    {
+    }
+    else if (command.equals("repositoryconnection/checkstatus"))
+    {
+    }
+    else if (command.equals("authorityconnection/list"))
+    {
+    }
+    else if (command.equals("authorityconnection/get"))
+    {
+    }
+    else if (command.equals("authorityconnection/save"))
+    {
+    }
+    else if (command.equals("authorityconnection/delete"))
+    {
+    }
+    else if (command.equals("authorityconnection/checkstatus"))
+    {
+    }
+    else if (command.equals("report/documentstatus"))
+    {
+    }
+    else if (command.equals("report/queuestatus"))
+    {
+    }
+    else if (command.equals("report/simplehistory"))
+    {
+    }
+    else if (command.equals("report/maximumbandwidth"))
+    {
+    }
+    else if (command.equals("report/maximumactivity"))
+    {
+    }
+    else if (command.equals("report/resultsummary"))
+    {
+    }
+    else
+    {
+      ConfigurationNode error = new ConfigurationNode(API_ERRORNODE);
+      error.setValue("Unrecognized command: "+command);
+      rval.addChild(rval.getChildCount(),error);
+    }
+    return rval;
+  }
+  
+  // Job node types
+  protected static final String JOBNODE_ID = "id";
+  protected static final String JOBNODE_DESCRIPTION = "description";
+  protected static final String JOBNODE_CONNECTIONNAME = "repository_connection";
+  protected static final String JOBNODE_OUTPUTNAME = "output_connection";
+  protected static final String JOBNODE_DOCUMENTSPECIFICATION = "document_specification";
+  protected static final String JOBNODE_OUTPUTSPECIFICATION = "output_specification";
+  protected static final String JOBNODE_STARTMODE = "start_mode";
+  protected static final String JOBNODE_RUNMODE = "run_mode";
+  protected static final String JOBNODE_HOPCOUNTMODE = "hopcount_mode";
+  protected static final String JOBNODE_PRIORITY = "priority";
+  protected static final String JOBNODE_RECRAWLINTERVAL = "recrawl_interval";
+  protected static final String JOBNODE_EXPIRATIONINTERVAL = "expiration_interval";
+  protected static final String JOBNODE_RESEEDINTERVAL = "reseed_interval";
+  protected static final String JOBNODE_SCHEDULE = "schedule";
+  protected static final String JOBNODE_RECORD = "record";
+  protected static final String JOBNODE_TIMEZONE = "timezone";
+  protected static final String JOBNODE_DURATION = "duration";
+  protected static final String JOBNODE_DAYOFWEEK = "dayofweek";
+  protected static final String JOBNODE_MONTHOFYEAR = "monthofyear";
+  protected static final String JOBNODE_DAYOFMONTH = "dayofmonth";
+  protected static final String JOBNODE_YEAR = "year";
+  protected static final String JOBNODE_HOUROFDAY = "hourofday";
+  protected static final String JOBNODE_MINUTESOFHOUR = "minutesofhour";
+  protected static final String JOBNODE_ENUMVALUE = "value";
+  
+  /** Convert a node into a job description */
+  protected static IJobDescription readJobDescription(IThreadContext tc, ConfigurationNode jobNode)
+    throws LCFException
+  {
+    org.apache.lcf.crawler.jobs.JobDescription jobDescription = new org.apache.lcf.crawler.jobs.JobDescription();
+    // Walk through the node's children
+    int i = 0;
+    while (i < jobNode.getChildCount())
+    {
+      ConfigurationNode child = jobNode.findChild(i++);
+      String childType = child.getType();
+      if (childType.equals(JOBNODE_ID))
+      {
+        if (child.getValue() == null)
+          throw new LCFException("Job id node requires a value");
+        jobDescription.setID(new Long(child.getValue()));
+      }
+      else if (childType.equals(JOBNODE_DESCRIPTION))
+      {
+        jobDescription.setDescription(child.getValue());
+      }
+      else if (childType.equals(JOBNODE_CONNECTIONNAME))
+      {
+        jobDescription.setConnectionName(child.getValue());
+      }
+      else if (childType.equals(JOBNODE_OUTPUTNAME))
+      {
+        jobDescription.setOutputConnectionName(child.getValue());
+      }
+      else if (childType.equals(JOBNODE_DOCUMENTSPECIFICATION))
+      {
+        // Get the job's document specification, clear out the children, and copy new ones from the child.
+        DocumentSpecification ds = jobDescription.getSpecification();
+        ds.clearChildren();
+        int j = 0;
+        while (j < child.getChildCount())
+        {
+          ConfigurationNode cn = child.findChild(j++);
+          ds.addChild(ds.getChildCount(),new SpecificationNode(cn));
+        }
+      }
+      else if (childType.equals(JOBNODE_OUTPUTSPECIFICATION))
+      {
+        // Get the job's output specification, clear out the children, and copy new ones from the child.
+        OutputSpecification os = jobDescription.getOutputSpecification();
+        os.clearChildren();
+        int j = 0;
+        while (j < child.getChildCount())
+        {
+          ConfigurationNode cn = child.findChild(j++);
+          os.addChild(os.getChildCount(),new SpecificationNode(cn));
+        }
+      }
+      else if (childType.equals(JOBNODE_STARTMODE))
+      {
+        jobDescription.setStartMethod(mapToStartMode(child.getValue()));
+      }
+      else if (childType.equals(JOBNODE_RUNMODE))
+      {
+        jobDescription.setType(mapToRunMode(child.getValue()));
+      }
+      else if (childType.equals(JOBNODE_HOPCOUNTMODE))
+      {
+        jobDescription.setHopcountMode(mapToHopcountMode(child.getValue()));
+      }
+      else if (childType.equals(JOBNODE_PRIORITY))
+      {
+        try
+        {
+          jobDescription.setPriority(Integer.parseInt(child.getValue()));
+        }
+        catch (NumberFormatException e)
+        {
+          throw new LCFException(e.getMessage(),e);
+        }
+      }
+      else if (childType.equals(JOBNODE_RECRAWLINTERVAL))
+      {
+        jobDescription.setInterval(interpretInterval(child.getValue()));
+      }
+      else if (childType.equals(JOBNODE_EXPIRATIONINTERVAL))
+      {
+        jobDescription.setExpiration(interpretInterval(child.getValue()));
+      }
+      else if (childType.equals(JOBNODE_RESEEDINTERVAL))
+      {
+        jobDescription.setReseedInterval(interpretInterval(child.getValue()));
+      }
+      else if (childType.equals(JOBNODE_SCHEDULE))
+      {
+        // The children of this node should be schedule records.  Walk through them.
+        int k = 0;
+        while (k < child.getChildCount())
+        {
+          ConfigurationNode scheduleNode = child.findChild(k++);
+          if (scheduleNode.getType().equals(JOBNODE_RECORD))
+          {
+            // Create a schedule record.
+            String timezone = null;
+            Long duration = null;
+            EnumeratedValues dayOfWeek = null;
+            EnumeratedValues monthOfYear = null;
+            EnumeratedValues dayOfMonth = null;
+            EnumeratedValues year = null;
+            EnumeratedValues hourOfDay = null;
+            EnumeratedValues minutesOfHour = null;
+            
+            // Now, walk through children of the schedule node.
+            int q = 0;
+            while (q < scheduleNode.getChildCount())
+            {
+              ConfigurationNode scheduleField = scheduleNode.findChild(q++);
+              String fieldType = scheduleField.getType();
+              if (fieldType.equals(JOBNODE_TIMEZONE))
+              {
+                timezone = scheduleField.getValue();
+              }
+              else if (fieldType.equals(JOBNODE_DURATION))
+              {
+                duration = new Long(scheduleField.getValue());
+              }
+              else if (fieldType.equals(JOBNODE_DAYOFWEEK))
+              {
+                dayOfWeek = processEnumeratedValues(scheduleField);
+              }
+              else if (fieldType.equals(JOBNODE_MONTHOFYEAR))
+              {
+                monthOfYear = processEnumeratedValues(scheduleField);
+              }
+              else if (fieldType.equals(JOBNODE_YEAR))
+              {
+                year = processEnumeratedValues(scheduleField);
+              }
+              else if (fieldType.equals(JOBNODE_DAYOFMONTH))
+              {
+                dayOfMonth = processEnumeratedValues(scheduleField);
+              }
+              else if (fieldType.equals(JOBNODE_HOUROFDAY))
+              {
+                hourOfDay = processEnumeratedValues(scheduleField);
+              }
+              else if (fieldType.equals(JOBNODE_MINUTESOFHOUR))
+              {
+                minutesOfHour = processEnumeratedValues(scheduleField);
+              }
+              else
+                throw new LCFException("Unrecognized field in schedule record: '"+fieldType+"'");
+            }
+            ScheduleRecord sr = new ScheduleRecord(dayOfWeek,monthOfYear,dayOfMonth,year,hourOfDay,minutesOfHour,timezone,duration);
+            // Add the schedule record to the job.
+            jobDescription.addScheduleRecord(sr);
+          }
+          else
+            throw new LCFException("Encountered an unexpected node type in schedule: '"+scheduleNode.getType()+"'");
+        }
+      }
+      else
+        throw new LCFException("Unrecognized job field: '"+childType+"'");
+    }
+    if (jobDescription.getID() == null)
+    {
+      jobDescription.setID(new Long(IDFactory.make(tc)));
+      jobDescription.setIsNew(true);
+    }
+    else
+      jobDescription.setIsNew(false);
+    return jobDescription;
+  }
+
+  /** Convert a job description into a ConfigurationNode */
+  protected static ConfigurationNode formatJobDescription(String nodeType, IJobDescription job)
+  {
+    ConfigurationNode jobNode = new ConfigurationNode(nodeType);
+    // For each field of the job, add an appropriate child node, with value.
+    ConfigurationNode child;
+    int j;
+    
+    // id
+    if (job.getID() != null)
+    {
+      child = new ConfigurationNode(JOBNODE_ID);
+      child.setValue(job.getID().toString());
+      jobNode.addChild(jobNode.getChildCount(),child);
+    }
+    
+    // description
+    if (job.getDescription() != null)
+    {
+      child = new ConfigurationNode(JOBNODE_DESCRIPTION);
+      child.setValue(job.getDescription());
+      jobNode.addChild(jobNode.getChildCount(),child);
+    }
+    
+    // connection
+    if (job.getConnectionName() != null)
+    {
+      child = new ConfigurationNode(JOBNODE_CONNECTIONNAME);
+      child.setValue(job.getConnectionName());
+      jobNode.addChild(jobNode.getChildCount(),child);
+    }
+
+    // output connection
+    if (job.getOutputConnectionName() != null)
+    {
+      child = new ConfigurationNode(JOBNODE_OUTPUTNAME);
+      child.setValue(job.getOutputConnectionName());
+      jobNode.addChild(jobNode.getChildCount(),child);
+    }
+
+    // Document specification
+    DocumentSpecification ds = job.getSpecification();
+    child = new ConfigurationNode(JOBNODE_DOCUMENTSPECIFICATION);
+    j = 0;
+    while (j < ds.getChildCount())
+    {
+      ConfigurationNode cn = ds.getChild(j++);
+      child.addChild(child.getChildCount(),cn);
+    }
+    jobNode.addChild(jobNode.getChildCount(),child);
+
+    // Output specification
+    OutputSpecification os = job.getOutputSpecification();
+    child = new ConfigurationNode(JOBNODE_OUTPUTSPECIFICATION);
+    j = 0;
+    while (j < os.getChildCount())
+    {
+      ConfigurationNode cn = os.getChild(j++);
+      child.addChild(child.getChildCount(),cn);
+    }
+    jobNode.addChild(jobNode.getChildCount(),child);
+
+    // Start mode
+    child = new ConfigurationNode(JOBNODE_STARTMODE);
+    child.setValue(startModeMap(job.getStartMethod()));
+    jobNode.addChild(jobNode.getChildCount(),child);
+
+    // Run mode
+    child = new ConfigurationNode(JOBNODE_RUNMODE);
+    child.setValue(runModeMap(job.getType()));
+    jobNode.addChild(jobNode.getChildCount(),child);
+
+    // Hopcount mode
+    child = new ConfigurationNode(JOBNODE_HOPCOUNTMODE);
+    child.setValue(hopcountModeMap(job.getHopcountMode()));
+    jobNode.addChild(jobNode.getChildCount(),child);
+
+    // Priority
+    child = new ConfigurationNode(JOBNODE_PRIORITY);
+    child.setValue(Integer.toString(job.getPriority()));
+    jobNode.addChild(jobNode.getChildCount(),child);
+
+    // Recrawl interval
+    if (job.getInterval() != null)
+    {
+      child = new ConfigurationNode(JOBNODE_RECRAWLINTERVAL);
+      child.setValue(job.getInterval().toString());
+      jobNode.addChild(jobNode.getChildCount(),child);
+    }
+    
+    // Expiration interval
+    if (job.getExpiration() != null)
+    {
+      child = new ConfigurationNode(JOBNODE_EXPIRATIONINTERVAL);
+      child.setValue(job.getExpiration().toString());
+      jobNode.addChild(jobNode.getChildCount(),child);
+    }
+    
+    // Reseed interval
+    if (job.getReseedInterval() != null)
+    {
+      child = new ConfigurationNode(JOBNODE_RESEEDINTERVAL);
+      child.setValue(job.getReseedInterval().toString());
+      jobNode.addChild(jobNode.getChildCount(),child);
+    }
+    
+    // Schedule records
+    child = new ConfigurationNode(JOBNODE_SCHEDULE);
+    j = 0;
+    while (j < job.getScheduleRecordCount())
+    {
+      ScheduleRecord sr = job.getScheduleRecord(j++);
+      ConfigurationNode recordNode = new ConfigurationNode(JOBNODE_RECORD);
+      ConfigurationNode recordChild;
+      
+      // timezone
+      if (sr.getTimezone() != null)
+      {
+        recordChild = new ConfigurationNode(JOBNODE_TIMEZONE);
+        recordChild.setValue(sr.getTimezone());
+        recordNode.addChild(recordNode.getChildCount(),recordChild);
+      }
+
+      // duration
+      if (sr.getDuration() != null)
+      {
+        recordChild = new ConfigurationNode(JOBNODE_DURATION);
+        recordChild.setValue(sr.getDuration().toString());
+        recordNode.addChild(recordNode.getChildCount(),recordChild);
+      }
+      
+      // Schedule specification values
+      
+      // day of week
+      if (sr.getDayOfWeek() != null)
+        formatEnumeratedValues(recordNode,JOBNODE_DAYOFWEEK,sr.getDayOfWeek());
+      if (sr.getMonthOfYear() != null)
+        formatEnumeratedValues(recordNode,JOBNODE_MONTHOFYEAR,sr.getMonthOfYear());
+      if (sr.getDayOfMonth() != null)
+        formatEnumeratedValues(recordNode,JOBNODE_DAYOFMONTH,sr.getDayOfMonth());
+      if (sr.getYear() != null)
+        formatEnumeratedValues(recordNode,JOBNODE_YEAR,sr.getYear());
+      if (sr.getHourOfDay() != null)
+        formatEnumeratedValues(recordNode,JOBNODE_HOUROFDAY,sr.getHourOfDay());
+      if (sr.getMinutesOfHour() != null)
+        formatEnumeratedValues(recordNode,JOBNODE_MINUTESOFHOUR,sr.getMinutesOfHour());
+
+      child.addChild(child.getChildCount(),recordNode);
+    }
+    jobNode.addChild(jobNode.getChildCount(),child);
+    
+    return jobNode;
+  }
+
+  protected static void formatEnumeratedValues(ConfigurationNode recordNode, String childType, EnumeratedValues value)
+  {
+    ConfigurationNode child = new ConfigurationNode(childType);
+    Iterator iter = value.getValues();
+    while (iter.hasNext())
+    {
+      Integer theValue = (Integer)iter.next();
+      ConfigurationNode valueNode = new ConfigurationNode(JOBNODE_ENUMVALUE);
+      valueNode.setValue(theValue.toString());
+      child.addChild(child.getChildCount(),valueNode);
+    }
+    recordNode.addChild(recordNode.getChildCount(),child);
+  }
+  
+  protected static EnumeratedValues processEnumeratedValues(ConfigurationNode fieldNode)
+    throws LCFException
+  {
+    ArrayList values = new ArrayList();
+    int i = 0;
+    while (i < fieldNode.getChildCount())
+    {
+      ConfigurationNode cn = fieldNode.findChild(i++);
+      if (cn.getType().equals(JOBNODE_ENUMVALUE))
+      {
+        try
+        {
+          values.add(new Integer(cn.getValue()));
+        }
+        catch (NumberFormatException e)
+        {
+          throw new LCFException("Error processing enumerated value node: "+e.getMessage(),e);
+        }
+      }
+      else
+        throw new LCFException("Error processing enumerated value nodes: Unrecognized node type '"+cn.getType()+"'");
+    }
+    return new EnumeratedValues(values);
+  }
+  
+  protected static String presentInterval(Long interval)
+  {
+    if (interval == null)
+      return "infinite";
+    return interval.toString();
+  }
+
+  protected static Long interpretInterval(String interval)
+    throws LCFException
+  {
+    if (interval == null || interval.equals("infinite"))
+      return null;
+    else
+      return new Long(interval);
+  }
+  
+  protected static String startModeMap(int startMethod)
+  {
+    switch (startMethod)
+    {
+    case IJobDescription.START_WINDOWBEGIN:
+      return "schedule window start";
+    case IJobDescription.START_WINDOWINSIDE:
+      return "schedule window anytime";
+    case IJobDescription.START_DISABLE:
+      return "manual";
+    default:
+      return "unknown";
+    }
+  }
+
+  protected static int mapToStartMode(String startMethod)
+    throws LCFException
+  {
+    if (startMethod.equals("schedule window start"))
+      return IJobDescription.START_WINDOWBEGIN;
+    else if (startMethod.equals("schedule window anytime"))
+      return IJobDescription.START_WINDOWINSIDE;
+    else if (startMethod.equals("manual"))
+      return IJobDescription.START_DISABLE;
+    else
+      throw new LCFException("Unrecognized start method: '"+startMethod+"'");
+  }
+  
+  protected static String runModeMap(int type)
+  {
+    switch (type)
+    {
+    case IJobDescription.TYPE_CONTINUOUS:
+      return "continuous";
+    case IJobDescription.TYPE_SPECIFIED:
+      return "scan once";
+    default:
+      return "unknown";
+    }
+  }
+
+  protected static int mapToRunMode(String mode)
+    throws LCFException
+  {
+    if (mode.equals("continuous"))
+      return IJobDescription.TYPE_CONTINUOUS;
+    else if (mode.equals("scan once"))
+      return IJobDescription.TYPE_SPECIFIED;
+    else
+      throw new LCFException("Unrecognized run method: '"+mode+"'");
+  }
+  
+  protected static String hopcountModeMap(int mode)
+  {
+    switch (mode)
+    {
+    case IJobDescription.HOPCOUNT_ACCURATE:
+      return "accurate";
+    case IJobDescription.HOPCOUNT_NODELETE:
+      return "no delete";
+    case IJobDescription.HOPCOUNT_NEVERDELETE:
+      return "never delete";
+    default:
+      return "unknown";
+    }
+  }
+
+  protected static int mapToHopcountMode(String mode)
+    throws LCFException
+  {
+    if (mode.equals("accurate"))
+      return IJobDescription.HOPCOUNT_ACCURATE;
+    else if (mode.equals("no delete"))
+      return IJobDescription.HOPCOUNT_NODELETE;
+    else if (mode.equals("never delete"))
+      return IJobDescription.HOPCOUNT_NEVERDELETE;
+    else
+      throw new LCFException("Unrecognized hopcount method: '"+mode+"'");
+  }
+  
+  protected static ConfigurationNode findConfigurationNode(Configuration input, String argumentName)
+  {
+    // Look for argument among the children
+    int i = 0;
+    while (i < input.getChildCount())
+    {
+      ConfigurationNode cn = input.findChild(i++);
+      if (cn.getType().equals(argumentName))
+        return cn;
+    }
+    return null;
+
+  }
+  
+  protected static String getRootArgument(Configuration input, String argumentName)
+  {
+    ConfigurationNode node = findConfigurationNode(input,argumentName);
+    if (node == null)
+      return null;
+    return node.getValue();
+  }
+  
+  protected static class ResponseValue extends Configuration
+  {
+    /** Constructor.
+    */
+    public ResponseValue()
+    {
+      super();
+    }
+
+    /** Return the root node type.
+    *@return the node type name.
+    */
+    protected String getRootNodeLabel()
+    {
+      return "response";
+    }
+    
+    /** Create a new object of the appropriate class.
+    */
+    protected Configuration createNew()
+    {
+      return new ResponseValue();
+    }
+
+  }
+  
 }
 

Added: incubator/lcf/trunk/modules/json/org/json/CDL.java
URL: http://svn.apache.org/viewvc/incubator/lcf/trunk/modules/json/org/json/CDL.java?rev=964035&view=auto
==============================================================================
--- incubator/lcf/trunk/modules/json/org/json/CDL.java (added)
+++ incubator/lcf/trunk/modules/json/org/json/CDL.java Wed Jul 14 12:55:26 2010
@@ -0,0 +1,279 @@
+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/**
+ * This provides static methods to convert comma delimited text into a
+ * JSONArray, and to covert a JSONArray into comma delimited text. Comma
+ * delimited text is a very popular format for data interchange. It is
+ * understood by most database, spreadsheet, and organizer programs.
+ * <p>
+ * Each row of text represents a row in a table or a data record. Each row
+ * ends with a NEWLINE character. Each row contains one or more values.
+ * Values are separated by commas. A value can contain any character except
+ * for comma, unless is is wrapped in single quotes or double quotes.
+ * <p>
+ * The first row usually contains the names of the columns.
+ * <p>
+ * A comma delimited list can be converted into a JSONArray of JSONObjects.
+ * The names for the elements in the JSONObjects can be taken from the names
+ * in the first row.
+ * @author JSON.org
+ * @version 2009-09-11
+ */
+public class CDL {
+
+    /**
+     * Get the next value. The value can be wrapped in quotes. The value can
+     * be empty.
+     * @param x A JSONTokener of the source text.
+     * @return The value string, or null if empty.
+     * @throws JSONException if the quoted string is badly formed.
+     */
+    private static String getValue(JSONTokener x) throws JSONException {
+        char c;
+        char q;
+        StringBuffer sb;
+        do {
+            c = x.next();
+        } while (c == ' ' || c == '\t');
+        switch (c) {
+        case 0:
+            return null;
+        case '"':
+        case '\'':
+        	q = c;
+        	sb = new StringBuffer();
+        	for (;;) {
+        		c = x.next();
+        		if (c == q) {
+        			break;
+        		}
+                if (c == 0 || c == '\n' || c == '\r') {
+                    throw x.syntaxError("Missing close quote '" + q + "'.");
+                }
+                sb.append(c);
+        	}
+            return sb.toString();
+        case ',':
+            x.back();
+            return "";
+        default:
+            x.back();
+            return x.nextTo(',');
+        }
+    }
+
+    /**
+     * Produce a JSONArray of strings from a row of comma delimited values.
+     * @param x A JSONTokener of the source text.
+     * @return A JSONArray of strings.
+     * @throws JSONException
+     */
+    public static JSONArray rowToJSONArray(JSONTokener x) throws JSONException {
+        JSONArray ja = new JSONArray();
+        for (;;) {
+            String value = getValue(x);
+            char c = x.next();
+            if (value == null || 
+            		(ja.length() == 0 && value.length() == 0 && c != ',')) {
+                return null;
+            }
+            ja.put(value);
+            for (;;) {                
+                if (c == ',') {
+                    break;
+                }
+                if (c != ' ') {
+                    if (c == '\n' || c == '\r' || c == 0) {
+                        return ja;
+                    }
+                    throw x.syntaxError("Bad character '" + c + "' (" +
+                            (int)c + ").");
+                }
+                c = x.next();
+            }
+        }
+    }
+
+    /**
+     * Produce a JSONObject from a row of comma delimited text, using a
+     * parallel JSONArray of strings to provides the names of the elements.
+     * @param names A JSONArray of names. This is commonly obtained from the
+     *  first row of a comma delimited text file using the rowToJSONArray
+     *  method.
+     * @param x A JSONTokener of the source text.
+     * @return A JSONObject combining the names and values.
+     * @throws JSONException
+     */
+    public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x)
+            throws JSONException {
+        JSONArray ja = rowToJSONArray(x);
+        return ja != null ? ja.toJSONObject(names) :  null;
+    }
+
+    /**
+     * Produce a JSONArray of JSONObjects from a comma delimited text string,
+     * using the first row as a source of names.
+     * @param string The comma delimited text.
+     * @return A JSONArray of JSONObjects.
+     * @throws JSONException
+     */
+    public static JSONArray toJSONArray(String string) throws JSONException {
+        return toJSONArray(new JSONTokener(string));
+    }
+
+    /**
+     * Produce a JSONArray of JSONObjects from a comma delimited text string,
+     * using the first row as a source of names.
+     * @param x The JSONTokener containing the comma delimited text.
+     * @return A JSONArray of JSONObjects.
+     * @throws JSONException
+     */
+    public static JSONArray toJSONArray(JSONTokener x) throws JSONException {
+        return toJSONArray(rowToJSONArray(x), x);
+    }
+
+    /**
+     * Produce a JSONArray of JSONObjects from a comma delimited text string
+     * using a supplied JSONArray as the source of element names.
+     * @param names A JSONArray of strings.
+     * @param string The comma delimited text.
+     * @return A JSONArray of JSONObjects.
+     * @throws JSONException
+     */
+    public static JSONArray toJSONArray(JSONArray names, String string)
+            throws JSONException {
+        return toJSONArray(names, new JSONTokener(string));
+    }
+
+    /**
+     * Produce a JSONArray of JSONObjects from a comma delimited text string
+     * using a supplied JSONArray as the source of element names.
+     * @param names A JSONArray of strings.
+     * @param x A JSONTokener of the source text.
+     * @return A JSONArray of JSONObjects.
+     * @throws JSONException
+     */
+    public static JSONArray toJSONArray(JSONArray names, JSONTokener x)
+            throws JSONException {
+        if (names == null || names.length() == 0) {
+            return null;
+        }
+        JSONArray ja = new JSONArray();
+        for (;;) {
+            JSONObject jo = rowToJSONObject(names, x);
+            if (jo == null) {
+                break;
+            }
+            ja.put(jo);
+        }
+        if (ja.length() == 0) {
+            return null;
+        }
+        return ja;
+    }
+
+
+    /**
+     * Produce a comma delimited text row from a JSONArray. Values containing
+     * the comma character will be quoted. Troublesome characters may be 
+     * removed.
+     * @param ja A JSONArray of strings.
+     * @return A string ending in NEWLINE.
+     */
+    public static String rowToString(JSONArray ja) {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < ja.length(); i += 1) {
+            if (i > 0) {
+                sb.append(',');
+            }
+            Object o = ja.opt(i);
+            if (o != null) {
+                String s = o.toString();
+                if (s.length() > 0 && (s.indexOf(',') >= 0 || s.indexOf('\n') >= 0 || 
+                		s.indexOf('\r') >= 0 || s.indexOf(0) >= 0 || 
+                		s.charAt(0) == '"')) {
+                    sb.append('"');
+                	int length = s.length();
+                	for (int j = 0; j < length; j += 1) {
+                		char c = s.charAt(j);
+                		if (c >= ' ' && c != '"') {
+                			sb.append(c);
+                		}
+                    }
+                    sb.append('"');
+                } else {
+                    sb.append(s);
+                }
+            }
+        }
+        sb.append('\n');
+        return sb.toString();
+    }
+
+    /**
+     * Produce a comma delimited text from a JSONArray of JSONObjects. The
+     * first row will be a list of names obtained by inspecting the first
+     * JSONObject.
+     * @param ja A JSONArray of JSONObjects.
+     * @return A comma delimited text.
+     * @throws JSONException
+     */
+    public static String toString(JSONArray ja) throws JSONException {
+        JSONObject jo = ja.optJSONObject(0);
+        if (jo != null) {
+            JSONArray names = jo.names();
+            if (names != null) {
+                return rowToString(names) + toString(names, ja);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Produce a comma delimited text from a JSONArray of JSONObjects using
+     * a provided list of names. The list of names is not included in the
+     * output.
+     * @param names A JSONArray of strings.
+     * @param ja A JSONArray of JSONObjects.
+     * @return A comma delimited text.
+     * @throws JSONException
+     */
+    public static String toString(JSONArray names, JSONArray ja)
+            throws JSONException {
+        if (names == null || names.length() == 0) {
+            return null;
+        }
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < ja.length(); i += 1) {
+            JSONObject jo = ja.optJSONObject(i);
+            if (jo != null) {
+                sb.append(rowToString(jo.toJSONArray(names)));
+            }
+        }
+        return sb.toString();
+    }
+}

Propchange: incubator/lcf/trunk/modules/json/org/json/CDL.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/lcf/trunk/modules/json/org/json/CDL.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: incubator/lcf/trunk/modules/json/org/json/Cookie.java
URL: http://svn.apache.org/viewvc/incubator/lcf/trunk/modules/json/org/json/Cookie.java?rev=964035&view=auto
==============================================================================
--- incubator/lcf/trunk/modules/json/org/json/Cookie.java (added)
+++ incubator/lcf/trunk/modules/json/org/json/Cookie.java Wed Jul 14 12:55:26 2010
@@ -0,0 +1,169 @@
+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/**
+ * Convert a web browser cookie specification to a JSONObject and back.
+ * JSON and Cookies are both notations for name/value pairs.
+ * @author JSON.org
+ * @version 2008-09-18
+ */
+public class Cookie {
+
+    /**
+     * Produce a copy of a string in which the characters '+', '%', '=', ';'
+     * and control characters are replaced with "%hh". This is a gentle form
+     * of URL encoding, attempting to cause as little distortion to the
+     * string as possible. The characters '=' and ';' are meta characters in
+     * cookies. By convention, they are escaped using the URL-encoding. This is
+     * only a convention, not a standard. Often, cookies are expected to have
+     * encoded values. We encode '=' and ';' because we must. We encode '%' and
+     * '+' because they are meta characters in URL encoding.
+     * @param string The source string.
+     * @return       The escaped result.
+     */
+    public static String escape(String string) {
+        char         c;
+        String       s = string.trim();
+        StringBuffer sb = new StringBuffer();
+        int          len = s.length();
+        for (int i = 0; i < len; i += 1) {
+            c = s.charAt(i);
+            if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') {
+                sb.append('%');
+                sb.append(Character.forDigit((char)((c >>> 4) & 0x0f), 16));
+                sb.append(Character.forDigit((char)(c & 0x0f), 16));
+            } else {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+
+
+    /**
+     * Convert a cookie specification string into a JSONObject. The string
+     * will contain a name value pair separated by '='. The name and the value
+     * will be unescaped, possibly converting '+' and '%' sequences. The
+     * cookie properties may follow, separated by ';', also represented as
+     * name=value (except the secure property, which does not have a value).
+     * The name will be stored under the key "name", and the value will be
+     * stored under the key "value". This method does not do checking or
+     * validation of the parameters. It only converts the cookie string into
+     * a JSONObject.
+     * @param string The cookie specification string.
+     * @return A JSONObject containing "name", "value", and possibly other
+     *  members.
+     * @throws JSONException
+     */
+    public static JSONObject toJSONObject(String string) throws JSONException {
+        String         n;
+        JSONObject     o = new JSONObject();
+        Object         v;
+        JSONTokener x = new JSONTokener(string);
+        o.put("name", x.nextTo('='));
+        x.next('=');
+        o.put("value", x.nextTo(';'));
+        x.next();
+        while (x.more()) {
+            n = unescape(x.nextTo("=;"));
+            if (x.next() != '=') {
+                if (n.equals("secure")) {
+                    v = Boolean.TRUE;
+                } else {
+                    throw x.syntaxError("Missing '=' in cookie parameter.");
+                }
+            } else {
+                v = unescape(x.nextTo(';'));
+                x.next();
+            }
+            o.put(n, v);
+        }
+        return o;
+    }
+
+
+    /**
+     * Convert a JSONObject into a cookie specification string. The JSONObject
+     * must contain "name" and "value" members.
+     * If the JSONObject contains "expires", "domain", "path", or "secure"
+     * members, they will be appended to the cookie specification string.
+     * All other members are ignored.
+     * @param o A JSONObject
+     * @return A cookie specification string
+     * @throws JSONException
+     */
+    public static String toString(JSONObject o) throws JSONException {
+        StringBuffer sb = new StringBuffer();
+
+        sb.append(escape(o.getString("name")));
+        sb.append("=");
+        sb.append(escape(o.getString("value")));
+        if (o.has("expires")) {
+            sb.append(";expires=");
+            sb.append(o.getString("expires"));
+        }
+        if (o.has("domain")) {
+            sb.append(";domain=");
+            sb.append(escape(o.getString("domain")));
+        }
+        if (o.has("path")) {
+            sb.append(";path=");
+            sb.append(escape(o.getString("path")));
+        }
+        if (o.optBoolean("secure")) {
+            sb.append(";secure");
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Convert <code>%</code><i>hh</i> sequences to single characters, and
+     * convert plus to space.
+     * @param s A string that may contain
+     *      <code>+</code>&nbsp;<small>(plus)</small> and
+     *      <code>%</code><i>hh</i> sequences.
+     * @return The unescaped string.
+     */
+    public static String unescape(String s) {
+        int len = s.length();
+        StringBuffer b = new StringBuffer();
+        for (int i = 0; i < len; ++i) {
+            char c = s.charAt(i);
+            if (c == '+') {
+                c = ' ';
+            } else if (c == '%' && i + 2 < len) {
+                int d = JSONTokener.dehexchar(s.charAt(i + 1));
+                int e = JSONTokener.dehexchar(s.charAt(i + 2));
+                if (d >= 0 && e >= 0) {
+                    c = (char)(d * 16 + e);
+                    i += 2;
+                }
+            }
+            b.append(c);
+        }
+        return b.toString();
+    }
+}

Propchange: incubator/lcf/trunk/modules/json/org/json/Cookie.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/lcf/trunk/modules/json/org/json/Cookie.java
------------------------------------------------------------------------------
    svn:keywords = Id