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 2011/07/07 14:44:12 UTC

svn commit: r1143796 - in /incubator/lcf/branches/CONNECTORS-221/connectors/cmis: ./ connector/ connector/src/ connector/src/main/ connector/src/main/java/ connector/src/main/java/org/ connector/src/main/java/org/apache/ connector/src/main/java/org/apa...

Author: kwright
Date: Thu Jul  7 12:44:11 2011
New Revision: 1143796

URL: http://svn.apache.org/viewvc?rev=1143796&view=rev
Log:
Add CMIS repository connector.  Build environment etc to follow.

Added:
    incubator/lcf/branches/CONNECTORS-221/connectors/cmis/
    incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/
    incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/
    incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/
    incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/
    incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/
    incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/
    incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/
    incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/
    incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/
    incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/
    incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/CmisRepositoryConnector.java   (with props)

Added: incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/CmisRepositoryConnector.java
URL: http://svn.apache.org/viewvc/incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/CmisRepositoryConnector.java?rev=1143796&view=auto
==============================================================================
--- incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/CmisRepositoryConnector.java (added)
+++ incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/CmisRepositoryConnector.java Thu Jul  7 12:44:11 2011
@@ -0,0 +1,999 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.manifoldcf.crawler.connectors.cmis;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.rmi.NotBoundException;
+import java.rmi.RemoteException;
+import java.text.SimpleDateFormat;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.chemistry.opencmis.client.api.CmisObject;
+import org.apache.chemistry.opencmis.client.api.Document;
+import org.apache.chemistry.opencmis.client.api.Folder;
+import org.apache.chemistry.opencmis.client.api.ItemIterable;
+import org.apache.chemistry.opencmis.client.api.Property;
+import org.apache.chemistry.opencmis.client.api.QueryResult;
+import org.apache.chemistry.opencmis.client.api.Repository;
+import org.apache.chemistry.opencmis.client.api.Session;
+import org.apache.chemistry.opencmis.client.api.SessionFactory;
+import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl;
+import org.apache.chemistry.opencmis.commons.PropertyIds;
+import org.apache.chemistry.opencmis.commons.SessionParameter;
+import org.apache.chemistry.opencmis.commons.enums.BindingType;
+import org.apache.chemistry.opencmis.commons.enums.PropertyType;
+import org.apache.chemistry.opencmis.commons.impl.Constants;
+import org.apache.commons.lang.StringUtils;
+import org.apache.manifoldcf.agents.interfaces.RepositoryDocument;
+import org.apache.manifoldcf.agents.interfaces.ServiceInterruption;
+import org.apache.manifoldcf.core.interfaces.ConfigParams;
+import org.apache.manifoldcf.core.interfaces.IHTTPOutput;
+import org.apache.manifoldcf.core.interfaces.IKeystoreManager;
+import org.apache.manifoldcf.core.interfaces.IPostParameters;
+import org.apache.manifoldcf.core.interfaces.IThreadContext;
+import org.apache.manifoldcf.core.interfaces.KeystoreManagerFactory;
+import org.apache.manifoldcf.core.interfaces.ManifoldCFException;
+import org.apache.manifoldcf.core.interfaces.SpecificationNode;
+import org.apache.manifoldcf.crawler.connectors.BaseRepositoryConnector;
+import org.apache.manifoldcf.crawler.interfaces.DocumentSpecification;
+import org.apache.manifoldcf.crawler.interfaces.IProcessActivity;
+import org.apache.manifoldcf.crawler.interfaces.ISeedingActivity;
+import org.apache.manifoldcf.crawler.system.Logging;
+
+/**
+ * This is the "repository connector" for a CMIS-compliant repository.
+ * 
+ * @author Piergiorgio Lucidi
+ */
+public class CmisRepositoryConnector extends BaseRepositoryConnector {
+
+  public static final String CONFIG_PARAM_USERNAME = "username";
+  public static final String CONFIG_PARAM_PASSWORD = "password";
+  public static final String CONFIG_PARAM_ENDPOINT = "endpoint";
+  public static final String CONFIG_PARAM_REPOSITORY_ID = "repositoryId";
+  public static final String CONFIG_PARAM_CMIS_QUERY = "cmisQuery";
+
+  private static final String JOB_STARTPOINT_NODE_TYPE = "startpoint";
+  private static final String TAB_LABEL_CMIS_QUERY = "CMIS Query";
+
+  protected final static String ACTIVITY_READ = "read document";
+  protected static final String RELATIONSHIP_CHILD = "child";
+
+  private static final String CMIS_FOLDER_BASE_TYPE = "Folder";
+  private static final SimpleDateFormat ISO8601_DATE_FORMATTER = new SimpleDateFormat(
+      "yyyy-MM-dd'T'HH:mm:ssZ");
+
+  /**
+   * CMIS Session handle
+   */
+  Session session = null;
+
+  protected String username = null;
+  protected String password = null;
+  protected String endpoint = null;
+  protected String repositoryId = null;
+
+  protected SessionFactory factory = SessionFactoryImpl.newInstance();
+  protected Map<String, String> parameters = new HashMap<String, String>();
+
+  public final static String ACTIVITY_FETCH = "fetch";
+
+  protected static final long timeToRelease = 300000L;
+  protected long lastSessionFetch = -1L;
+
+  public CmisRepositoryConnector() {
+    super();
+  }
+
+  @Override
+  public String[] getActivitiesList() {
+    return new String[] { ACTIVITY_FETCH };
+  }
+
+  @Override
+  public String[] getBinNames(String documentIdentifier) {
+    return new String[] { endpoint };
+  }
+
+  protected class GetSessionThread extends Thread {
+    protected Throwable exception = null;
+
+    public GetSessionThread() {
+      super();
+      setDaemon(true);
+    }
+
+    public void run() {
+      try {
+        // Create a session
+        parameters.clear();
+
+        // user credentials
+        parameters.put(SessionParameter.USER, username);
+        parameters.put(SessionParameter.PASSWORD, password);
+
+        // connection settings
+        parameters.put(SessionParameter.ATOMPUB_URL, endpoint);
+        parameters.put(SessionParameter.BINDING_TYPE,
+            BindingType.ATOMPUB.value());
+
+        // create session
+        if (StringUtils.isEmpty(repositoryId)) {
+
+          // get a session from the first CMIS repository exposed by
+          // the endpoint
+          List<Repository> repos = factory.getRepositories(parameters);
+          session = repos.get(0).createSession();
+        } else {
+
+          // get a session from the repository specified in the
+          // configuration with its own ID
+          parameters.put(SessionParameter.REPOSITORY_ID, repositoryId);
+          session = factory.createSession(parameters);
+        }
+
+      } catch (Throwable e) {
+        this.exception = e;
+      }
+    }
+
+    public Throwable getException() {
+      return exception;
+    }
+  }
+
+  protected class CheckConnectionThread extends Thread {
+    protected Throwable exception = null;
+
+    public CheckConnectionThread() {
+      super();
+      setDaemon(true);
+    }
+
+    public void run() {
+      try {
+        session.getRootFolder();
+      } catch (Throwable e) {
+        this.exception = e;
+      }
+    }
+
+    public Throwable getException() {
+      return exception;
+    }
+
+  }
+
+  protected class DestroySessionThread extends Thread {
+    protected Throwable exception = null;
+
+    public DestroySessionThread() {
+      super();
+      setDaemon(true);
+    }
+
+    public void run() {
+      try {
+        session.clear();
+      } catch (Throwable e) {
+        this.exception = e;
+      }
+    }
+
+    public Throwable getException() {
+      return exception;
+    }
+
+  }
+
+  @Override
+  public void disconnect() throws ManifoldCFException {
+    if (session != null) {
+      DestroySessionThread t = new DestroySessionThread();
+      try {
+        t.start();
+        t.join();
+        Throwable thr = t.getException();
+        if (thr != null) {
+          if (thr instanceof RemoteException)
+            throw (RemoteException) thr;
+          else
+            throw (Error) thr;
+        }
+        session = null;
+        lastSessionFetch = -1L;
+      } catch (InterruptedException e) {
+        t.interrupt();
+        throw new ManifoldCFException("Interrupted: " + e.getMessage(), e,
+            ManifoldCFException.INTERRUPTED);
+      } catch (RemoteException e) {
+        Throwable e2 = e.getCause();
+        if (e2 instanceof InterruptedException
+            || e2 instanceof InterruptedIOException)
+          throw new ManifoldCFException(e2.getMessage(), e2,
+              ManifoldCFException.INTERRUPTED);
+        session = null;
+        lastSessionFetch = -1L;
+        // Treat this as a transient problem
+        Logging.connectors.warn(
+            "Cmis: Transient remote exception closing session: "
+                + e.getMessage(), e);
+      }
+
+    }
+
+    username = null;
+    password = null;
+    endpoint = null;
+    repositoryId = null;
+
+  }
+
+  /**
+   * This method create a new CMIS session for a CMIS repository, if the
+   * repositoryId is not provided in the configuration, the connector will
+   * retrieve all the repositories exposed for this endpoint the it will start
+   * to use the first one.
+   */
+  @Override
+  public void connect(ConfigParams configParams) {
+    super.connect(configParams);
+    username = params.getParameter(CONFIG_PARAM_USERNAME);
+    password = params.getParameter(CONFIG_PARAM_PASSWORD);
+    endpoint = params.getParameter(CONFIG_PARAM_ENDPOINT);
+    if (StringUtils.isNotEmpty(params.getParameter(CONFIG_PARAM_REPOSITORY_ID)))
+      repositoryId = params.getParameter(CONFIG_PARAM_REPOSITORY_ID);
+  }
+
+  @Override
+  public String check() throws ManifoldCFException {
+    try {
+      checkConnection();
+      return super.check();
+    } catch (ServiceInterruption e) {
+      return "Connection temporarily failed: " + e.getMessage();
+    } catch (ManifoldCFException e) {
+      return "Connection failed: " + e.getMessage();
+    }
+  }
+
+  protected void getSession() throws ManifoldCFException, ServiceInterruption {
+    if (session == null) {
+      // Check for parameter validity
+      if (StringUtils.isEmpty(username))
+        throw new ManifoldCFException("Parameter " + CONFIG_PARAM_USERNAME
+            + " required but not set");
+
+      if (Logging.connectors.isDebugEnabled())
+        Logging.connectors.debug("Cmis: Username = '" + username + "'");
+
+      if (StringUtils.isEmpty(password))
+        throw new ManifoldCFException("Parameter " + CONFIG_PARAM_PASSWORD
+            + " required but not set");
+
+      Logging.connectors.debug("Cmis: Password exists");
+
+      if (StringUtils.isEmpty(endpoint))
+        throw new ManifoldCFException("Parameter " + CONFIG_PARAM_ENDPOINT
+            + " required but not set");
+
+      long currentTime;
+      GetSessionThread t = new GetSessionThread();
+      try {
+        t.start();
+        t.join();
+        Throwable thr = t.getException();
+        if (thr != null) {
+          if (thr instanceof java.net.MalformedURLException)
+            throw (java.net.MalformedURLException) thr;
+          else if (thr instanceof NotBoundException)
+            throw (NotBoundException) thr;
+          else if (thr instanceof RemoteException)
+            throw (RemoteException) thr;
+          else
+            throw (Error) thr;
+        }
+      } catch (InterruptedException e) {
+        t.interrupt();
+        throw new ManifoldCFException("Interrupted: " + e.getMessage(), e,
+            ManifoldCFException.INTERRUPTED);
+      } catch (java.net.MalformedURLException e) {
+        throw new ManifoldCFException(e.getMessage(), e);
+      } catch (NotBoundException e) {
+        // Transient problem: Server not available at the moment.
+        Logging.connectors.warn(
+            "Cmis: CMIS server not up at the moment: " + e.getMessage(), e);
+        currentTime = System.currentTimeMillis();
+        throw new ServiceInterruption(e.getMessage(), currentTime + 60000L);
+      } catch (RemoteException e) {
+        Throwable e2 = e.getCause();
+        if (e2 instanceof InterruptedException
+            || e2 instanceof InterruptedIOException)
+          throw new ManifoldCFException(e2.getMessage(), e2,
+              ManifoldCFException.INTERRUPTED);
+        // Treat this as a transient problem
+        Logging.connectors.warn(
+            "Cmis: Transient remote exception creating session: "
+                + e.getMessage(), e);
+        currentTime = System.currentTimeMillis();
+        throw new ServiceInterruption(e.getMessage(), currentTime + 60000L);
+      }
+
+    }
+
+    lastSessionFetch = System.currentTimeMillis();
+  }
+
+  /**
+   * Release the session, if it's time.
+   */
+  protected void releaseCheck() throws ManifoldCFException {
+    if (lastSessionFetch == -1L)
+      return;
+
+    long currentTime = System.currentTimeMillis();
+    if (currentTime >= lastSessionFetch + timeToRelease) {
+      DestroySessionThread t = new DestroySessionThread();
+      try {
+        t.start();
+        t.join();
+        Throwable thr = t.getException();
+        if (thr != null) {
+          if (thr instanceof RemoteException)
+            throw (RemoteException) thr;
+          else
+            throw (Error) thr;
+        }
+        session = null;
+        lastSessionFetch = -1L;
+      } catch (InterruptedException e) {
+        t.interrupt();
+        throw new ManifoldCFException("Interrupted: " + e.getMessage(), e,
+            ManifoldCFException.INTERRUPTED);
+      } catch (RemoteException e) {
+        Throwable e2 = e.getCause();
+        if (e2 instanceof InterruptedException
+            || e2 instanceof InterruptedIOException)
+          throw new ManifoldCFException(e2.getMessage(), e2,
+              ManifoldCFException.INTERRUPTED);
+        session = null;
+        lastSessionFetch = -1L;
+        // Treat this as a transient problem
+        Logging.connectors.warn(
+            "Cmis: Transient remote exception closing session: "
+                + e.getMessage(), e);
+      }
+
+    }
+  }
+
+  protected void checkConnection() throws ManifoldCFException,
+      ServiceInterruption {
+    while (true) {
+      boolean noSession = (session == null);
+      getSession();
+      long currentTime;
+      CheckConnectionThread t = new CheckConnectionThread();
+      try {
+        t.start();
+        t.join();
+        Throwable thr = t.getException();
+        if (thr != null) {
+          if (thr instanceof RemoteException)
+            throw (RemoteException) thr;
+          else
+            throw (Error) thr;
+        }
+        return;
+      } catch (InterruptedException e) {
+        t.interrupt();
+        throw new ManifoldCFException("Interrupted: " + e.getMessage(), e,
+            ManifoldCFException.INTERRUPTED);
+      } catch (RemoteException e) {
+        Throwable e2 = e.getCause();
+        if (e2 instanceof InterruptedException
+            || e2 instanceof InterruptedIOException)
+          throw new ManifoldCFException(e2.getMessage(), e2,
+              ManifoldCFException.INTERRUPTED);
+        if (noSession) {
+          currentTime = System.currentTimeMillis();
+          throw new ServiceInterruption(
+              "Transient error connecting to filenet service: "
+                  + e.getMessage(), currentTime + 60000L);
+        }
+        session = null;
+        lastSessionFetch = -1L;
+        continue;
+      }
+    }
+  }
+
+  @Override
+  public void poll() throws ManifoldCFException {
+    if (lastSessionFetch == -1L)
+      return;
+
+    long currentTime = System.currentTimeMillis();
+    if (currentTime >= lastSessionFetch + timeToRelease) {
+      DestroySessionThread t = new DestroySessionThread();
+      try {
+        t.start();
+        t.join();
+        Throwable thr = t.getException();
+        if (thr != null) {
+          if (thr instanceof RemoteException)
+            throw (RemoteException) thr;
+          else
+            throw (Error) thr;
+        }
+        session = null;
+        lastSessionFetch = -1L;
+      } catch (InterruptedException e) {
+        t.interrupt();
+        throw new ManifoldCFException("Interrupted: " + e.getMessage(), e,
+            ManifoldCFException.INTERRUPTED);
+      } catch (RemoteException e) {
+        Throwable e2 = e.getCause();
+        if (e2 instanceof InterruptedException
+            || e2 instanceof InterruptedIOException)
+          throw new ManifoldCFException(e2.getMessage(), e2,
+              ManifoldCFException.INTERRUPTED);
+        session = null;
+        lastSessionFetch = -1L;
+        // Treat this as a transient problem
+        Logging.connectors.warn(
+            "Cmis: Transient remote exception closing session: "
+                + e.getMessage(), e);
+      }
+
+    }
+  }
+
+  @Override
+  public void addSeedDocuments(ISeedingActivity activities,
+      DocumentSpecification spec, long startTime, long endTime, int jobMode)
+      throws ManifoldCFException, ServiceInterruption {
+
+    getSession();
+
+    String cmisQuery = StringUtils.EMPTY;
+    int i = 0;
+    while (i < spec.getChildCount()) {
+      SpecificationNode sn = spec.getChild(i);
+      if (sn.getType().equals(JOB_STARTPOINT_NODE_TYPE)) {
+        cmisQuery = sn.getAttributeValue(CONFIG_PARAM_CMIS_QUERY);
+        break;
+      }
+    }
+
+    if (StringUtils.isEmpty(cmisQuery)) {
+      // get root Documents from the Cmis Repository
+      ItemIterable<CmisObject> cmisObjects = session.getRootFolder()
+          .getChildren();
+      for (CmisObject cmisObject : cmisObjects) {
+        activities.addSeedDocument(cmisObject.getId());
+      }
+    } else {
+      ItemIterable<QueryResult> results = session.query(cmisQuery, false);
+      for (QueryResult result : results) {
+        String id = result.getPropertyValueById(PropertyIds.OBJECT_ID);
+        activities.addSeedDocument(id);
+      }
+    }
+
+  }
+
+  @Override
+  public int getMaxDocumentRequest() {
+    return 1;
+  }
+
+  /**
+   * Return the list of relationship types that this connector recognizes.
+   * 
+   * @return the list.
+   */
+  @Override
+  public String[] getRelationshipTypes() {
+    return new String[] { RELATIONSHIP_CHILD };
+  }
+
+  /**
+   * View configuration. This method is called in the body section of the
+   * connector's view configuration page. Its purpose is to present the
+   * connection information to the user. The coder can presume that the HTML that
+   * is output from this configuration will be within appropriate <html> and
+   * <body> tags.
+   * 
+   * @param threadContext
+   *          is the local thread context.
+   * @param out
+   *          is the output to which any HTML should be sent.
+   * @param parameters
+   *          are the configuration parameters, as they currently exist, for
+   *          this connection being configured.
+   */
+  @Override
+  public void viewConfiguration(IThreadContext threadContext, IHTTPOutput out,
+      ConfigParams parameters) throws ManifoldCFException, IOException {
+    out.print("<table class=\"displaytable\">\n"
+        + "  <tr>\n"
+        + "    <td class=\"description\" colspan=\"1\"><nobr>Parameters:</nobr></td>\n"
+        + "    <td class=\"value\" colspan=\"3\">\n");
+    Iterator iter = parameters.listParameters();
+    while (iter.hasNext()) {
+      String param = (String) iter.next();
+      String value = parameters.getParameter(param);
+      if (param.length() >= "password".length()
+          && param.substring(param.length() - "password".length())
+              .equalsIgnoreCase("password")) {
+        out.print("      <nobr>"
+            + org.apache.manifoldcf.ui.util.Encoder.bodyEscape(param)
+            + "=********</nobr><br/>\n");
+      } else if (param.length() >= "keystore".length()
+          && param.substring(param.length() - "keystore".length())
+              .equalsIgnoreCase("keystore")) {
+        IKeystoreManager kmanager = KeystoreManagerFactory.make("", value);
+        out.print("      <nobr>"
+            + org.apache.manifoldcf.ui.util.Encoder.bodyEscape(param) + "=<"
+            + Integer.toString(kmanager.getContents().length)
+            + " certificate(s)></nobr><br/>\n");
+      } else {
+        out.print("      <nobr>"
+            + org.apache.manifoldcf.ui.util.Encoder.bodyEscape(param) + "="
+            + org.apache.manifoldcf.ui.util.Encoder.bodyEscape(value)
+            + "</nobr><br/>\n");
+      }
+    }
+    out.print("    </td>\n" + "  </tr>\n" + "</table>\n");
+  }
+
+  /**
+   * Output the configuration header section. This method is called in the head
+   * section of the connector's configuration page. Its purpose is to add the
+   * required tabs to the list, and to output any javascript methods that might
+   * be needed by the configuration editing HTML.
+   * 
+   * @param threadContext
+   *          is the local thread context.
+   * @param out
+   *          is the output to which any HTML should be sent.
+   * @param parameters
+   *          are the configuration parameters, as they currently exist, for
+   *          this connection being configured.
+   * @param tabsArray
+   *          is an array of tab names. Add to this array any tab names that are
+   *          specific to the connector.
+   */
+  @Override
+  public void outputConfigurationHeader(IThreadContext threadContext,
+      IHTTPOutput out, ConfigParams parameters, List<String> tabsArray)
+      throws ManifoldCFException, IOException {
+
+    out.print("<script type=\"text/javascript\">\n" + "<!--\n"
+        + "function checkConfig()\n" + "{\n"
+        + "  if (editconnection.username.value == \"\")\n" + "  {\n"
+        + "    alert(\"The username must be not null\");\n"
+        + "    editconnection.username.focus();\n" + "    return false;\n"
+        + "  }\n" + "  if (editconnection.password.value == \"\")\n" + "  {\n"
+        + "    alert(\"The password must be not null\");\n"
+        + "    editconnection.password.focus();\n" + "    return false;\n"
+        + "  }\n" + "  if (editconnection.endpoint.value == \"\")\n" + "  {\n"
+        + "    alert(\"The endpoint must be not null\");\n"
+        + "    editconnection.endpoint.focus();\n" + "    return false;\n"
+        + "  }\n" + "\n" + "  return true;\n" + "}\n" + "	\n"
+        + "function checkConfigForSave()\n" + "{\n"
+        + "  if (editconnection.username.value == \"\")\n" + "  {\n"
+        + "    alert(\"The username must be not null\");\n"
+        + "    editconnection.username.focus();\n" + "    return false;\n"
+        + "  }\n" + "  if (editconnection.password.value == \"\")\n" + "  {\n"
+        + "    alert(\"The password must be not null\");\n"
+        + "    editconnection.password.focus();\n" + "    return false;\n"
+        + "  }\n" + "  if (editconnection.endpoint.value == \"\")\n" + "  {\n"
+        + "    alert(\"The endpoint must be not null\");\n"
+        + "    editconnection.endpoint.focus();\n" + "    return false;\n"
+        + "  }\n" + "  return true;\n" + "}\n" + "\n" + "//-->\n"
+        + "</script>\n");
+  }
+
+  @Override
+  public void outputConfigurationBody(IThreadContext threadContext,
+      IHTTPOutput out, ConfigParams parameters, String tabName)
+      throws ManifoldCFException, IOException {
+    out.print("<table class=\"displaytable\">\n"
+        + "  <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n");
+    out.print("<tr><td>Username: <input type=\"text\" name=\""
+        + CONFIG_PARAM_USERNAME + "\"/></td></tr>\n");
+    out.print("<tr><td>Password: <input type=\"password\" name=\""
+        + CONFIG_PARAM_PASSWORD + "\"/></td></tr>\n");
+    out.print("<tr><td>Endpoint: <input type=\"text\" name=\""
+        + CONFIG_PARAM_ENDPOINT + "\"/></td></tr>\n");
+    out.print("<tr><td>Repository ID: <input type=\"text\" name=\""
+        + CONFIG_PARAM_REPOSITORY_ID + "\"/></td></tr>\n");
+    out.print("</table>\n");
+  }
+
+  /**
+   * Process a configuration post. This method is called at the start of the
+   * connector's configuration page, whenever there is a possibility that form
+   * data for a connection has been posted. Its purpose is to gather form
+   * information and modify the configuration parameters accordingly. The name
+   * of the posted form is "editconnection".
+   * 
+   * @param threadContext
+   *          is the local thread context.
+   * @param variableContext
+   *          is the set of variables available from the post, including binary
+   *          file post information.
+   * @param parameters
+   *          are the configuration parameters, as they currently exist, for
+   *          this connection being configured.
+   * @return null if all is well, or a string error message if there is an error
+   *         that should prevent saving of the connection (and cause a
+   *         redirection to an error page).
+   */
+  @Override
+  public String processConfigurationPost(IThreadContext threadContext,
+      IPostParameters variableContext, ConfigParams parameters)
+      throws ManifoldCFException {
+    String username = variableContext.getParameter(CONFIG_PARAM_USERNAME);
+    if (StringUtils.isNotEmpty(username))
+      parameters.setParameter(CONFIG_PARAM_USERNAME, username);
+
+    String password = variableContext.getParameter(CONFIG_PARAM_PASSWORD);
+    if (StringUtils.isNotEmpty(password))
+      parameters.setParameter(CONFIG_PARAM_PASSWORD, password);
+
+    String endpoint = variableContext.getParameter(CONFIG_PARAM_ENDPOINT);
+    if (StringUtils.isNotEmpty(endpoint) && endpoint.length() > 0)
+      parameters.setParameter(CONFIG_PARAM_ENDPOINT, endpoint);
+
+    String repositoryId = variableContext
+        .getParameter(CONFIG_PARAM_REPOSITORY_ID);
+    if (StringUtils.isNotEmpty(repositoryId))
+      parameters.setParameter(CONFIG_PARAM_REPOSITORY_ID, repositoryId);
+
+    return null;
+  }
+
+  /**
+   * View specification. This method is called in the body section of a job's
+   * view page. Its purpose is to present the document specification information
+   * to the user. The coder can presume that the HTML that is output from this
+   * configuration will be within appropriate <html> and <body> tags.
+   * 
+   * @param out
+   *          is the output to which any HTML should be sent.
+   * @param ds
+   *          is the current document specification for this job.
+   */
+  @Override
+  public void viewSpecification(IHTTPOutput out, DocumentSpecification ds)
+      throws ManifoldCFException, IOException {
+
+    out.print("<table class=\"displaytable\">\n");
+    int i = 0;
+    boolean seenAny = false;
+    while (i < ds.getChildCount()) {
+      SpecificationNode sn = ds.getChild(i);
+      if (sn.getType().equals(JOB_STARTPOINT_NODE_TYPE)) {
+        if (seenAny == false) {
+          seenAny = true;
+        }
+        out.print("  <tr>\n"
+            + "    <td class=\"description\">CMIS Query:</td>\n"
+            + "    <td class=\"value\">\n"
+            + org.apache.manifoldcf.ui.util.Encoder.bodyEscape(sn
+                .getAttributeValue(CONFIG_PARAM_CMIS_QUERY)));
+        out.print("    </td>\n" + "  </tr>\n");
+
+      }
+      i++;
+    }
+
+    if (seenAny == false) {
+      out.print("  <tr><td class=\"message\">No documents specified</td></tr>\n");
+    }
+    out.print("</table>\n");
+
+  }
+
+  /**
+   * Process a specification post. This method is called at the start of job's
+   * edit or view page, whenever there is a possibility that form data for a
+   * connection has been posted. Its purpose is to gather form information and
+   * modify the document specification accordingly. The name of the posted form
+   * is "editjob".
+   * 
+   * @param variableContext
+   *          contains the post data, including binary file-upload information.
+   * @param ds
+   *          is the current document specification for this job.
+   * @return null if all is well, or a string error message if there is an error
+   *         that should prevent saving of the job (and cause a redirection to
+   *         an error page).
+   */
+  @Override
+  public String processSpecificationPost(IPostParameters variableContext,
+      DocumentSpecification ds) throws ManifoldCFException {
+    ds.clearChildren();
+
+    /**
+     * while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i);
+     * if (sn.getType().equals(JOB_STARTPOINT_NODE_TYPE)) { if (seenAny ==
+     * false) { seenAny = true; } out.print( "
+     * <tr>
+     * \n"+ "
+     * <td class=\"description\">CMIS Query:</td>\n"+ "
+     * <td class=\"value\">\n
+     * "+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(sn.getAttributeValue(CONFIG_PARAM_CMIS_QUERY)
+     * )
+     */
+    String cmisQuery = variableContext.getParameter(CONFIG_PARAM_CMIS_QUERY);
+    if (StringUtils.isNotEmpty(cmisQuery)) {
+      int i = 0;
+      while (i < ds.getChildCount()) {
+        SpecificationNode oldNode = ds.getChild(i);
+        if (oldNode.getType().equals(JOB_STARTPOINT_NODE_TYPE)) {
+          ds.removeChild(i);
+          break;
+        }
+      }
+      SpecificationNode node = new SpecificationNode(JOB_STARTPOINT_NODE_TYPE);
+      node.setAttribute(CONFIG_PARAM_CMIS_QUERY, cmisQuery);
+      variableContext.setParameter(CONFIG_PARAM_CMIS_QUERY, cmisQuery);
+      ds.addChild(ds.getChildCount(), node);
+    } else {
+      int i = 0;
+      while (i < ds.getChildCount()) {
+        SpecificationNode oldNode = ds.getChild(i);
+        if (oldNode.getType().equals(JOB_STARTPOINT_NODE_TYPE)) {
+          variableContext.setParameter(CONFIG_PARAM_CMIS_QUERY,
+              oldNode.getAttributeValue(CONFIG_PARAM_CMIS_QUERY));
+        }
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * Output the specification body section. This method is called in the body
+   * section of a job page which has selected a repository connection of the
+   * current type. Its purpose is to present the required form elements for
+   * editing. The coder can presume that the HTML that is output from this
+   * configuration will be within appropriate <html>, <body>, and <form> tags.
+   * The name of the form is "editjob".
+   * 
+   * @param out
+   *          is the output to which any HTML should be sent.
+   * @param ds
+   *          is the current document specification for this job.
+   * @param tabName
+   *          is the current tab name.
+   */
+  @Override
+  public void outputSpecificationBody(IHTTPOutput out,
+      DocumentSpecification ds, String tabName) throws ManifoldCFException,
+      IOException {
+    if (tabName.equals(TAB_LABEL_CMIS_QUERY)) {
+      String cmisQuery = StringUtils.EMPTY;
+      int i = 0;
+      while (i < ds.getChildCount()) {
+        SpecificationNode n = ds.getChild(i);
+        if (n.getType().equals(JOB_STARTPOINT_NODE_TYPE)) {
+          cmisQuery = n.getAttributeValue(CONFIG_PARAM_CMIS_QUERY);
+          break;
+        }
+      }
+
+      out.print("<table class=\"displaytable\">\n"
+          + "  <tr><td class=\"separator\" colspan=\"3\"><hr/></td></tr>\n");
+      out.print("        <tr class=\"formrow\"><td class=\"lightseparator\" colspan=\"3\"><hr/></td></tr>\n"
+          + "        <tr class=\"formrow\">\n"
+          + "          <td class=\"formcolumncell\">\n"
+          + "            <nobr>\n"
+          + "              <a name=\"cmisQuery\">\n"
+          + "                <input type=\"button\" value=\"Add\" onClick='Javascript:SpecOp(\"specop\",\"Add\",\"cmisQuery\")' alt=\"Set the new query\"/>\n"
+          + "                <input type=\"hidden\" name=\"specop\" value=\"\"/>\n"
+          + "              </a>\n"
+          + "            </nobr>\n"
+          + "          </td>\n"
+          + "          <td class=\"formcolumncell\">\n"
+          + "            <nobr>\n"
+          + "              <input type=\"text\" size=\"80\" name=\"cmisQuery\" value=\""
+          + cmisQuery
+          + "\"/>\n"
+          + "            </nobr>\n"
+          + "          </td>\n"
+          + "  			</tr>\n"
+          + "       <tr class=\"formrow\"><td class=\"lightseparator\" colspan=\"3\"><hr/></td></tr>\n"
+          + "</table>\n");
+    }
+  }
+
+  /**
+   * Output the specification header section. This method is called in the head
+   * section of a job page which has selected a repository connection of the
+   * current type. Its purpose is to add the required tabs to the list, and to
+   * output any javascript methods that might be needed by the job editing HTML.
+   * 
+   * @param out
+   *          is the output to which any HTML should be sent.
+   * @param ds
+   *          is the current document specification for this job.
+   * @param tabsArray
+   *          is an array of tab names. Add to this array any tab names that are
+   *          specific to the connector.
+   */
+  @Override
+  public void outputSpecificationHeader(IHTTPOutput out,
+      DocumentSpecification ds, List<String> tabsArray)
+      throws ManifoldCFException, IOException {
+    tabsArray.add(TAB_LABEL_CMIS_QUERY);
+
+    out.print("<script type=\"text/javascript\">\n"
+        + "function checkSpecification()\n" + "{\n"
+        + "  // Does nothing right now.\n" + "  return true;\n" + "}\n" + "\n"
+        + "function SpecOp(n, opValue, anchorvalue)\n" + "{\n"
+        + "  eval(\"editjob.\"+n+\".value = \\\"\"+opValue+\"\\\"\");\n"
+        + "  postFormSetAnchor(anchorvalue);\n" + "}\n" + "</script>\n");
+
+  }
+
+  @Override
+  public void processDocuments(String[] documentIdentifiers, String[] versions,
+      IProcessActivity activities, DocumentSpecification spec,
+      boolean[] scanOnly) throws ManifoldCFException, ServiceInterruption {
+
+    getSession();
+    Logging.connectors.debug("Cmis: Inside processDocuments");
+    int i = 0;
+
+    while (i < documentIdentifiers.length) {
+      long startTime = System.currentTimeMillis();
+      String nodeId = documentIdentifiers[i];
+
+      if (Logging.connectors.isDebugEnabled())
+        Logging.connectors.debug("Cmis: Processing document identifier '"
+            + nodeId + "'");
+
+      CmisObject cmisObject = session.getObject(nodeId);
+
+      String errorCode = "OK";
+      String errorDesc = StringUtils.EMPTY;
+
+      if (cmisObject.getBaseType().getDisplayName()
+          .equals(CMIS_FOLDER_BASE_TYPE)) {
+
+        // adding all the children for a folder
+
+        Folder folder = (Folder) cmisObject;
+        ItemIterable<CmisObject> children = folder.getChildren();
+        for (CmisObject child : children) {
+          activities.addDocumentReference(child.getId(), nodeId,
+              RELATIONSHIP_CHILD);
+        }
+
+      } else {
+
+        // ingestion of all the contents
+
+        Document document = (Document) cmisObject;
+        long fileLength = document.getContentStreamLength();
+
+        InputStream is = document.getContentStream().getStream();
+
+        try {
+          RepositoryDocument rd = new RepositoryDocument();
+          rd.setBinary(is, fileLength);
+
+          List<Property<?>> properties = document.getProperties();
+          String id = StringUtils.EMPTY;
+          for (Property<?> property : properties) {
+            String propertyId = property.getId();
+            Object propertyValue = property.getValue();
+            if (propertyId.endsWith(Constants.PARAM_OBJECT_ID))
+              id = (String) propertyValue;
+
+            if (propertyValue != null) {
+              PropertyType propertyType = property.getType();
+
+              switch (propertyType) {
+
+              case STRING:
+              case ID:
+              case URI:
+              case HTML:
+                String stringValue = (String) propertyValue;
+                rd.addField(propertyId, stringValue);
+                break;
+
+              case BOOLEAN:
+                Boolean booleanValue = (Boolean) propertyValue;
+                rd.addField(propertyId, booleanValue.toString());
+                break;
+
+              case INTEGER:
+                BigInteger integerValue = (BigInteger) propertyValue;
+                rd.addField(propertyId, integerValue.toString());
+                break;
+
+              case DECIMAL:
+                BigDecimal decimalValue = (BigDecimal) propertyValue;
+                rd.addField(propertyId, decimalValue.toString());
+                break;
+
+              case DATETIME:
+                GregorianCalendar dateValue = (GregorianCalendar) propertyValue;
+                rd.addField(propertyId,
+                    ISO8601_DATE_FORMATTER.format(dateValue.getTime()));
+                break;
+
+              default:
+                break;
+              }
+            }
+          }
+
+          // Acl aclCmis = document.getAcl();
+          // List<Ace> aces = aclCmis.getAces();
+          // List acl = new ArrayList<String>();
+          //
+          // for (Ace ace : aces) {
+          // acl.addAll(ace.getPermissions());
+          // }
+          //
+          // String[] aclForManifold = new String[acl.size()];
+          // acl.toArray(aclForManifold);
+          // rd.setACL(aclForManifold);
+
+          String version = document.getVersionLabel();
+          activities.ingestDocument(id, version, id, rd);
+
+        } finally {
+          try {
+            is.close();
+          } catch (InterruptedIOException e) {
+            errorCode = "Interrupted error";
+            errorDesc = e.getMessage();
+            throw new ManifoldCFException(e.getMessage(), e,
+                ManifoldCFException.INTERRUPTED);
+          } catch (IOException e) {
+            errorCode = "IO ERROR";
+            errorDesc = e.getMessage();
+            Logging.connectors.warn(
+                "Cmis: IOException closing file input stream: "
+                    + e.getMessage(), e);
+          }
+
+          activities.recordActivity(new Long(startTime), ACTIVITY_READ,
+              fileLength, nodeId, errorCode, errorDesc, null);
+        }
+      }
+      i++;
+    }
+  }
+}
\ No newline at end of file

Propchange: incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/CmisRepositoryConnector.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/CmisRepositoryConnector.java
------------------------------------------------------------------------------
    svn:keywords = Id