/* $Id: MeridioConnector.java 996524 2010-09-13 13:38:01Z kwright $ */ /** * 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.meridio; import com.meridio.www.MeridioDMWS.DmLogicalOp; import com.meridio.www.MeridioDMWS.DmPermission; import com.meridio.www.MeridioDMWS.DmSearchScope; import com.meridio.www.MeridioDMWS.DmVersionInfo; import org.apache.manifoldcf.core.interfaces.*; import org.apache.manifoldcf.agents.interfaces.*; import org.apache.manifoldcf.meridio.DMSearchResults; import org.apache.manifoldcf.meridio.MeridioDataSetException; import org.apache.manifoldcf.meridio.MeridioWrapper; import org.apache.manifoldcf.crawler.interfaces.*; import org.apache.manifoldcf.crawler.system.Logging; import org.apache.manifoldcf.crawler.system.ManifoldCF; import org.apache.manifoldcf.connectorcommon.interfaces.*; import org.apache.http.conn.ConnectTimeoutException; import java.io.File; import java.io.InterruptedIOException; import java.io.IOException; import java.io.InputStream; import java.io.FileInputStream; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; import java.net.UnknownHostException; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.HashSet; import java.util.Map; import javax.xml.soap.SOAPException; import org.apache.axis.attachments.AttachmentPart; import org.apache.manifoldcf.crawler.connectors.meridio.DMDataSet.*; import org.apache.manifoldcf.crawler.connectors.meridio.RMDataSet.*; /** This is the "repository connector" for a file system. */ public class MeridioConnector extends org.apache.manifoldcf.crawler.connectors.BaseRepositoryConnector { public static final String _rcsid = "@(#)$Id: MeridioConnector.java 996524 2010-09-13 13:38:01Z kwright $"; // This is the base url to use. protected String urlBase = null; protected String urlVersionBase = null; private final static int maxHitsToReturn = 100; /** Deny access token for Meridio */ private final static String defaultAuthorityDenyToken = GLOBAL_DENY_TOKEN; private static final long interruptionRetryTime = 60000L; // These are the variables needed to establish a connection protected URL DmwsURL = null; protected URL RmwsURL = null; protected javax.net.ssl.SSLSocketFactory mySSLFactory = null; protected MeridioWrapper meridio_ = null; // A handle to the Meridio Java API Wrapper /** Constructor. */ public MeridioConnector() {} /** Tell the world what model this connector uses for getDocumentIdentifiers(). * This must return a model value as specified above. *@return the model type value. */ @Override public int getConnectorModel() { // Return the simplest model - full everything return MODEL_ADD_CHANGE; } /** Set up the session with Meridio */ protected void getSession() throws ManifoldCFException, ServiceInterruption { if (meridio_ == null) { // Do the first part (which used to be in connect() itself) try { /*================================================================= * Construct the URL strings from the parameters *================================================================*/ String DMWSProtocol = params.getParameter("DMWSServerProtocol"); String DMWSPort = params.getParameter("DMWSServerPort"); if (DMWSPort == null || DMWSPort.length() == 0) DMWSPort = ""; else DMWSPort = ":" + DMWSPort; String Url = DMWSProtocol + "://" + params.getParameter("DMWSServerName") + DMWSPort + params.getParameter("DMWSLocation"); Logging.connectors.debug("Meridio: Document Management Web Service (DMWS) URL is [" + Url + "]"); DmwsURL = new URL(Url); String RMWSProtocol = params.getParameter("RMWSServerProtocol"); String RMWSPort = params.getParameter("RMWSServerPort"); if (RMWSPort == null || RMWSPort.length() == 0) RMWSPort = ""; else RMWSPort = ":" + RMWSPort; Url = RMWSProtocol + "://" + params.getParameter("RMWSServerName") + RMWSPort + params.getParameter("RMWSLocation"); Logging.connectors.debug("Meridio: Record Management Web Service (RMWS) URL is [" + Url + "]"); RmwsURL = new URL(Url); // Set up ssl if indicated String keystoreData = params.getParameter( "MeridioKeystore" ); if (keystoreData != null) mySSLFactory = KeystoreManagerFactory.make("",keystoreData).getSecureSocketFactory(); else mySSLFactory = null; // Put together the url base String clientProtocol = params.getParameter("MeridioWebClientProtocol"); String clientPort = params.getParameter("MeridioWebClientServerPort"); if (clientPort == null || clientPort.length() == 0) clientPort = ""; else clientPort = ":"+clientPort; urlVersionBase = clientProtocol + "://" + params.getParameter("MeridioWebClientServerName") + clientPort + params.getParameter("MeridioWebClientDocDownloadLocation"); urlBase = urlVersionBase + "?launchMode=1&launchAs=0&documentId="; } catch (MalformedURLException malformedURLException) { throw new ManifoldCFException("Meridio: Could not construct the URL for either " + "the DM or RM Meridio Web Service", malformedURLException, ManifoldCFException.REPOSITORY_CONNECTION_ERROR); } // Do the second part (where we actually try to connect to the system) try { /*================================================================= * Now try and login to Meridio; the wrapper's constructor can be * used as it calls the Meridio login method *================================================================*/ meridio_ = new MeridioWrapper(Logging.connectors, DmwsURL, RmwsURL, null, params.getParameter("DMWSProxyHost"), params.getParameter("DMWSProxyPort"), params.getParameter("RMWSProxyHost"), params.getParameter("RMWSProxyPort"), null, null, params.getParameter("UserName"), params.getObfuscatedParameter("Password"), InetAddress.getLocalHost().getHostName(), mySSLFactory, org.apache.manifoldcf.connectorcommon.common.CommonsHTTPSender.class, "client-config.wsdd"); } catch (NumberFormatException e) { throw new ManifoldCFException("Meridio: bad number: "+e.getMessage(),e); } catch (UnknownHostException unknownHostException) { throw new ManifoldCFException("Meridio: A Unknown Host Exception occurred while " + "connecting - is a network software and hardware configuration: "+unknownHostException.getMessage(), unknownHostException); } catch (org.apache.axis.AxisFault e) { long currentTime = System.currentTimeMillis(); if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://xml.apache.org/axis/","HTTP"))) { org.w3c.dom.Element elem = e.lookupFaultDetail(new javax.xml.namespace.QName("http://xml.apache.org/axis/","HttpErrorCode")); if (elem != null) { elem.normalize(); String httpErrorCode = elem.getFirstChild().getNodeValue().trim(); throw new ManifoldCFException("Unexpected http error code "+httpErrorCode+" accessing Meridio: "+e.getMessage(),e); } throw new ManifoldCFException("Unknown http error occurred while connecting: "+e.getMessage(),e); } if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://schemas.xmlsoap.org/soap/envelope/","Server.userException"))) { String exceptionName = e.getFaultString(); if (exceptionName.equals("java.lang.InterruptedException")) throw new ManifoldCFException("Interrupted",ManifoldCFException.INTERRUPTED); } if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Got an unknown remote exception connecting - axis fault = "+e.getFaultCode().getLocalPart()+", detail = "+e.getFaultString()+" - retrying",e); throw new ServiceInterruption("Remote procedure exception: "+e.getMessage(), e, currentTime + 300000L, currentTime + 3 * 60 * 60000L,-1,false); } catch (RemoteException remoteException) { throw new ManifoldCFException("Meridio: An unknown remote exception occurred while " + "connecting: "+remoteException.getMessage(), remoteException); } } } /** Get the bin name string for a document identifier. The bin name describes the queue to which the * document will be assigned for throttling purposes. Throttling controls the rate at which items in a * given queue are fetched; it does not say anything about the overall fetch rate, which may operate on * multiple queues or bins. * For example, if you implement a web crawler, a good choice of bin name would be the server name, since * that is likely to correspond to a real resource that will need real throttle protection. *@param documentIdentifier is the document identifier. *@return the bin name. */ @Override public String[] getBinNames(String documentIdentifier) { String dmwshost = params.getParameter("DMWSServerName"); String rmwshost = params.getParameter("RMWSServerName"); return new String[]{dmwshost,rmwshost}; } /** Test the connection. Returns a string describing the connection integrity. *@return the connection's status as a displayable string. */ @Override public String check() throws ManifoldCFException { Logging.connectors.debug("Meridio: Entering 'check' method"); try { // Force a relogin meridio_ = null; getSession(); } catch (ServiceInterruption e) { return "Meridio temporarily unavailable: "+e.getMessage(); } catch (ManifoldCFException e) { return e.getMessage(); } try { /*================================================================= * Call a method in the Web Services API to get the Meridio system * name back - just something simple to test the connection * end-to-end *================================================================*/ DMDataSet ds = meridio_.getStaticData(); if (null == ds) { Logging.connectors.debug("Meridio: DM DataSet returned was null in 'check' method"); return "Connection failed - null DM DataSet"; } if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio System Name is [" + ds.getSYSTEMINFO().getSystemName() + "] and the comment is [" + ds.getSYSTEMINFO().getComment() + "]"); /*================================================================= * For completeness, we also call a method in the RM Web * Service API *================================================================*/ RMDataSet rmws = meridio_.getConfiguration(); if (null == rmws) { Logging.connectors.warn("Meridio: RM DataSet returned was null in 'check' method"); return "Connection failed - null RM DataSet returned"; } return super.check(); } catch (org.apache.axis.AxisFault e) { long currentTime = System.currentTimeMillis(); if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://xml.apache.org/axis/","HTTP"))) { org.w3c.dom.Element elem = e.lookupFaultDetail(new javax.xml.namespace.QName("http://xml.apache.org/axis/","HttpErrorCode")); if (elem != null) { elem.normalize(); String httpErrorCode = elem.getFirstChild().getNodeValue().trim(); return "Unexpected http error code "+httpErrorCode+" accessing Meridio: "+e.getMessage(); } return "Unknown http error occurred while checking: "+e.getMessage(); } if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://schemas.xmlsoap.org/soap/envelope/","Server.userException"))) { String exceptionName = e.getFaultString(); if (exceptionName.equals("java.lang.InterruptedException")) throw new ManifoldCFException("Interrupted",ManifoldCFException.INTERRUPTED); } if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Got an unknown remote exception checking - axis fault = "+e.getFaultCode().getLocalPart()+", detail = "+e.getFaultString()+" - retrying",e); return "Axis fault: "+e.getMessage(); } catch (RemoteException remoteException) { /*================================================================= * Log the exception because we will then discard it * * Potentially attempting to re-login may resolve this error but * if it is being called soon after a successful login, then that * is unlikely. * * A RemoteException could be a transient network error *================================================================*/ if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Unknown remote exception occurred during 'check' method: "+remoteException.getMessage(), remoteException); return "Connection failed - Remote exception: "+remoteException.getMessage(); } catch (MeridioDataSetException meridioDataSetException) { /*================================================================= * Log the exception because we will then discard it * * If it is a DataSet exception it means that we could not marshal * or unmarshall the XML returned from the Web Service call. This * means there is either a problem with the code, or perhaps the * connector is pointing at an incorrect/unsupported version of * Meridio *================================================================*/ if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: DataSet exception occurred during 'check' method: "+meridioDataSetException.getMessage(), meridioDataSetException); return "Connection failed - DataSet exception: "+meridioDataSetException.getMessage(); } finally { Logging.connectors.debug("Meridio: Exiting 'check' method"); } } /** Close the connection. Call this before discarding the repository connector. */ @Override public void disconnect() throws ManifoldCFException { Logging.connectors.debug("Meridio: Entering 'disconnect' method"); try { if (meridio_ != null) { meridio_.logout(); } } catch (org.apache.axis.AxisFault e) { long currentTime = System.currentTimeMillis(); if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://xml.apache.org/axis/","HTTP"))) { org.w3c.dom.Element elem = e.lookupFaultDetail(new javax.xml.namespace.QName("http://xml.apache.org/axis/","HttpErrorCode")); if (elem != null) { elem.normalize(); String httpErrorCode = elem.getFirstChild().getNodeValue().trim(); Logging.connectors.warn("Unexpected http error code "+httpErrorCode+" logging out: "+e.getMessage()); return; } Logging.connectors.warn("Unknown http error occurred while logging out: "+e.getMessage()); return; } if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://schemas.xmlsoap.org/soap/envelope/","Server.userException"))) { String exceptionName = e.getFaultString(); if (exceptionName.equals("java.lang.InterruptedException")) throw new ManifoldCFException("Interrupted",ManifoldCFException.INTERRUPTED); } if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://schemas.xmlsoap.org/soap/envelope/","Server"))) { if (e.getFaultString().indexOf(" 23031#") != -1) { // This means that the session has expired, so reset it and retry meridio_ = null; return; } } Logging.connectors.warn("Meridio: Got an unknown remote exception logging out - axis fault = "+e.getFaultCode().getLocalPart()+", detail = "+e.getFaultString(),e); return; } catch (RemoteException remoteException) { Logging.connectors.warn("Meridio: A remote exception occurred while " + "logging out: "+remoteException.getMessage(), remoteException); } finally { super.disconnect(); meridio_ = null; urlBase = null; urlVersionBase = null; DmwsURL = null; RmwsURL = null; mySSLFactory = null; Logging.connectors.debug("Meridio: Exiting 'disconnect' method"); } } /** Get the maximum number of documents to amalgamate together into one batch, for this connector. *@return the maximum number. 0 indicates "unlimited". */ @Override public int getMaxDocumentRequest() { return 10; } /** Request arbitrary connector information. * This method is called directly from the API in order to allow API users to perform any one of several connector-specific * queries. *@param output is the response object, to be filled in by this method. *@param command is the command, which is taken directly from the API request. *@return true if the resource is found, false if not. In either case, output may be filled in. */ @Override public boolean requestInfo(Configuration output, String command) throws ManifoldCFException { if (command.equals("categories")) { try { String[] categories = getMeridioCategories(); int i = 0; while (i < categories.length) { String category = categories[i++]; ConfigurationNode node = new ConfigurationNode("category"); node.setValue(category); output.addChild(output.getChildCount(),node); } } catch (ServiceInterruption e) { ManifoldCF.createServiceInterruptionNode(output,e); } catch (ManifoldCFException e) { ManifoldCF.createErrorNode(output,e); } } else if (command.equals("documentproperties")) { try { String[] properties = getMeridioDocumentProperties(); int i = 0; while (i < properties.length) { String property = properties[i++]; ConfigurationNode node = new ConfigurationNode("document_property"); node.setValue(property); output.addChild(output.getChildCount(),node); } } catch (ServiceInterruption e) { ManifoldCF.createServiceInterruptionNode(output,e); } catch (ManifoldCFException e) { ManifoldCF.createErrorNode(output,e); } } else if (command.startsWith("classorfolder/")) { String classOrFolderIdString = command.substring("classorfolder/".length()); int classOrFolderId; try { classOrFolderId = Integer.parseInt(classOrFolderIdString); } catch (NumberFormatException e) { ManifoldCF.createErrorNode(output,new ManifoldCFException(e.getMessage(),e)); return false; } try { MeridioClassContents[] contents = getClassOrFolderContents(classOrFolderId); int i = 0; while (i < contents.length) { MeridioClassContents content = contents[i++]; ConfigurationNode node = new ConfigurationNode("content"); ConfigurationNode child; child = new ConfigurationNode("id"); child.setValue(Integer.toString(content.classOrFolderId)); node.addChild(node.getChildCount(),child); child = new ConfigurationNode("name"); child.setValue(content.classOrFolderName); node.addChild(node.getChildCount(),child); child = new ConfigurationNode("type"); String typeString; if (content.containerType == MeridioClassContents.CLASS) typeString = "class"; else if (content.containerType == MeridioClassContents.FOLDER) typeString = "folder"; else typeString = "unknown"; child.setValue(typeString); node.addChild(node.getChildCount(),child); output.addChild(output.getChildCount(),node); } } catch (ServiceInterruption e) { ManifoldCF.createServiceInterruptionNode(output,e); } catch (ManifoldCFException e) { ManifoldCF.createErrorNode(output,e); } } else return super.requestInfo(output,command); return true; } /** Queue "seed" documents. Seed documents are the starting places for crawling activity. Documents * are seeded when this method calls appropriate methods in the passed in ISeedingActivity object. * * This method can choose to find repository changes that happen only during the specified time interval. * The seeds recorded by this method will be viewed by the framework based on what the * getConnectorModel() method returns. * * It is not a big problem if the connector chooses to create more seeds than are * strictly necessary; it is merely a question of overall work required. * * The end time and seeding version string passed to this method may be interpreted for greatest efficiency. * For continuous crawling jobs, this method will * be called once, when the job starts, and at various periodic intervals as the job executes. * * When a job's specification is changed, the framework automatically resets the seeding version string to null. The * seeding version string may also be set to null on each job run, depending on the connector model returned by * getConnectorModel(). * * Note that it is always ok to send MORE documents rather than less to this method. * The connector will be connected before this method can be called. *@param activities is the interface this method should use to perform whatever framework actions are desired. *@param spec is a document specification (that comes from the job). *@param seedTime is the end of the time range of documents to consider, exclusive. *@param lastSeedVersionString is the last seeding version string for this job, or null if the job has no previous seeding version string. *@param jobMode is an integer describing how the job is being run, whether continuous or once-only. *@return an updated seeding version string, to be stored with the job. */ @Override public String addSeedDocuments(ISeedingActivity activities, Specification spec, String lastSeedVersion, long seedTime, int jobMode) throws ManifoldCFException, ServiceInterruption { Logging.connectors.debug("Meridio: Entering 'addSeedDocuments' method"); long startTime; if (lastSeedVersion == null) startTime = 0L; else { // Unpack seed time from seed version string startTime = new Long(lastSeedVersion).longValue(); } // Adjust start time so that we don't miss documents that squeeze in with earlier timestamps after we've already scanned that interval. // Chose an interval of 15 minutes, but I've never seen this effect take place over a time interval even 1/10 of that. long timeAdjust = 15L * 60000L; if (startTime > timeAdjust) startTime -= timeAdjust; else startTime = 0L; while (true) { getSession(); try { DMSearchResults searchResults; int numResultsReturnedByStream = 0; while (true) { searchResults = documentSpecificationSearch(spec, startTime, seedTime, numResultsReturnedByStream + 1, maxHitsToReturn); for (int i = 0; i < searchResults.returnedHitsCount; i++) { long documentId = searchResults.dsDM.getSEARCHRESULTS_DOCUMENTS()[i].getDocId(); String strDocumentId = new Long(documentId).toString(); activities.addSeedDocument(strDocumentId); } numResultsReturnedByStream += searchResults.returnedHitsCount; if (numResultsReturnedByStream == searchResults.totalHitsCount) break; } return new Long(seedTime).toString(); } catch (org.apache.axis.AxisFault e) { long currentTime = System.currentTimeMillis(); if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://xml.apache.org/axis/","HTTP"))) { org.w3c.dom.Element elem = e.lookupFaultDetail(new javax.xml.namespace.QName("http://xml.apache.org/axis/","HttpErrorCode")); if (elem != null) { elem.normalize(); String httpErrorCode = elem.getFirstChild().getNodeValue().trim(); throw new ManifoldCFException("Unexpected http error code "+httpErrorCode+" accessing Meridio: "+e.getMessage(),e); } throw new ManifoldCFException("Unknown http error occurred while performing search: "+e.getMessage(),e); } if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://schemas.xmlsoap.org/soap/envelope/","Server.userException"))) { String exceptionName = e.getFaultString(); if (exceptionName.equals("java.lang.InterruptedException")) throw new ManifoldCFException("Interrupted",ManifoldCFException.INTERRUPTED); } if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://schemas.xmlsoap.org/soap/envelope/","Server"))) { if (e.getFaultString().indexOf(" 23031#") != -1) { // This means that the session has expired, so reset it and retry meridio_ = null; continue; } } if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Got an unknown remote exception while performing search - axis fault = "+e.getFaultCode().getLocalPart()+", detail = "+e.getFaultString()+" - retrying",e); throw new ServiceInterruption("Remote procedure exception: "+e.getMessage(), e, currentTime + 300000L, currentTime + 3 * 60 * 60000L,-1,false); } catch (RemoteException remoteException) { throw new ManifoldCFException("Meridio: A Remote Exception occurred while " + "performing a search: "+remoteException.getMessage(), remoteException); } catch (MeridioDataSetException meridioDataSetException) { throw new ManifoldCFException("Meridio: A problem occurred manipulating the Web " + "Service XML: "+meridioDataSetException.getMessage(), meridioDataSetException); } } } /** Process a set of documents. * This is the method that should cause each document to be fetched, processed, and the results either added * to the queue of documents for the current job, and/or entered into the incremental ingestion manager. * The document specification allows this class to filter what is done based on the job. * The connector will be connected before this method can be called. *@param documentIdentifiers is the set of document identifiers to process. *@param statuses are the currently-stored document versions for each document in the set of document identifiers * passed in above. *@param activities is the interface this method should use to queue up new document references * and ingest documents. *@param jobMode is an integer describing how the job is being run, whether continuous or once-only. *@param usesDefaultAuthority will be true only if the authority in use for these documents is the default one. */ @Override public void processDocuments(String[] documentIdentifiers, IExistingVersions statuses, Specification spec, IProcessActivity activities, int jobMode, boolean usesDefaultAuthority) throws ManifoldCFException, ServiceInterruption { // Get forced acls/security enable/disable String[] acls = getAcls(spec); // Sort it, in case it is needed. if (acls != null) java.util.Arrays.sort(acls); // Look at the metadata attributes. // So that the version strings are comparable, we will put them in an array first, and sort them. Set<String> holder = new HashSet<String>(); String pathAttributeName = null; MatchMap matchMap = new MatchMap(); boolean allMetadata = false; int i = 0; while (i < spec.getChildCount()) { SpecificationNode n = spec.getChild(i++); if (n.getType().equals("ReturnedMetadata")) { String category = n.getAttributeValue("category"); String attributeName = n.getAttributeValue("property"); String metadataName; if (category == null || category.length() == 0) metadataName = attributeName; else metadataName = category + "." + attributeName; holder.add(metadataName); } else if (n.getType().equals("AllMetadata")) { String value = n.getAttributeValue("value"); if (value != null && value.equals("true")) { allMetadata = true; } } else if (n.getType().equals("pathnameattribute")) pathAttributeName = n.getAttributeValue("value"); else if (n.getType().equals("pathmap")) { // Path mapping info also needs to be looked at, because it affects what is // ingested. String pathMatch = n.getAttributeValue("match"); String pathReplace = n.getAttributeValue("replace"); matchMap.appendMatchPair(pathMatch,pathReplace); } } while (true) { getSession(); // The version string returned must include everything that could affect what is ingested. In meridio's // case, this includes the date stamp, but it also includes the part of the specification that describes // the metadata desired. // The code here relies heavily on the search method to do it's thing. The search method originally // used the document specification to determine what metadata to return, which was problematic because that // meant this method had to modify the specification (not good practice), and was also wrong from the point // of view that we need to get the metadata specification appended to the version string in some way, and // use THAT data in processDocuments(). So I've broken all that up. try { // Put into an array ReturnMetadata[] categoryPropertyValues; String[] categoryPropertyStringValues; String[] sortArray; if (allMetadata) { categoryPropertyStringValues = getMeridioDocumentProperties(); } else { categoryPropertyStringValues = new String[holder.size()]; i = 0; for (String value : holder) { categoryPropertyStringValues[i++] = value; } } // Sort! java.util.Arrays.sort(categoryPropertyStringValues); categoryPropertyValues = new ReturnMetadata[categoryPropertyStringValues.length]; i = 0; for (String value : categoryPropertyStringValues) { int dotIndex = value.indexOf("."); String categoryName = null; String propertyName; if (dotIndex == -1) propertyName = value; else { categoryName = value.substring(0,dotIndex); propertyName = value.substring(dotIndex+1); } categoryPropertyValues[i++] = new ReturnMetadata(categoryName,propertyName); } // Prepare the part of the version string that is decodeable StringBuilder decodeableString = new StringBuilder(); // Add the metadata piece first packList(decodeableString,categoryPropertyStringValues,'+'); // Now, put in the forced acls. // The version string needs only to contain the forced acls, since the version date captures changes // made to the acls that are actually associated with the document. if (acls == null) decodeableString.append('-'); else { decodeableString.append('+'); packList(decodeableString,acls,'+'); decodeableString.append('+'); pack(decodeableString,defaultAuthorityDenyToken,'+'); } // Calculate the part of the version string that comes from path name and mapping. if (pathAttributeName != null) { decodeableString.append("+"); pack(decodeableString,pathAttributeName,'+'); pack(decodeableString,matchMap.toString(),'+'); } else decodeableString.append("-"); long[] docIds = new long[documentIdentifiers.length]; for (i = 0; i < documentIdentifiers.length; i++) { docIds[i] = new Long(documentIdentifiers[i]).longValue(); } /*================================================================= * Call the search, with the document specification and the list of * document ids - the search will never return more than exactly * one match per document id * * We are assuming that the maximum number of hits to return * should never be more than the maximum batch size set up for this * class * * We are just making one web service call (to the search API) * rather than iteratively calling a web service method for each * document passed in as part of the document array * * Additionally, re-using the same search method as for the * "getDocumentIdentifiers" method ensures that we are not * duplicating any logic which ensures that the document/records * in question match the search criteria or not. *================================================================*/ DMSearchResults searchResults = documentSpecificationSearch(spec, 0, 0, 1, this.getMaxDocumentRequest(), docIds, null); if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Found a total of <" + searchResults.totalHitsCount + "> hit(s) " + "and <" + searchResults.returnedHitsCount + "> were returned by the method call"); // If we are searching based on document identifier, then it is possible that we will not // find a document we are looking for, if it was removed from the system between the time // it was put in the queue and when it's version is obtained. Documents where this happens // should return a version string of null. // Let's go through the search results and build a hash based on the document identifier. Map<Long,SEARCHRESULTS_DOCUMENTS> documentMap = new HashMap<Long,SEARCHRESULTS_DOCUMENTS>(); if (searchResults.dsDM != null) { SEARCHRESULTS_DOCUMENTS [] srd = searchResults.dsDM.getSEARCHRESULTS_DOCUMENTS(); for (i = 0; i < srd.length; i++) { documentMap.put(new Long(srd[i].getDocId()),srd[i]); } } // Now, walk through the individual documents. Map<Long,String> versionStrings = new HashMap<Long,String>(); for (int j = 0; j < docIds.length; j++) { String documentIdentifier = documentIdentifiers[j]; long docId = docIds[j]; Long docKey = new Long(docId); // Look up the record. SEARCHRESULTS_DOCUMENTS doc = documentMap.get(docKey); if (doc != null) { // Set the version string. The parseable stuff goes first, so parsing is easy. String version = doc.getStr_value(); StringBuilder composedVersion = new StringBuilder(); composedVersion.append(decodeableString); composedVersion.append(version); // Added 9/7/2007 composedVersion.append("_").append(urlVersionBase); // String versionString = composedVersion.toString(); if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Document "+docKey+" has version "+versionString); if (activities.checkDocumentNeedsReindexing(documentIdentifier,versionString)) versionStrings.put(docKey,versionString); } else { if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Document "+docKey+" is no longer in the search set, or has been deleted - removing."); activities.deleteDocument(documentIdentifier); } } // Now submit search requests for all the documents requiring fetch. Map<Long,Map<String,String>> documentPropertyMap = new HashMap<Long,Map<String,String>>(); // Only look up metadata if we need some! if (versionStrings.size() > 0 && categoryPropertyValues.length > 0) { long[] fetchIds = new long[versionStrings.size()]; i = 0; for (Long docKey : versionStrings.keySet()) { fetchIds[i++] = docKey; } /*================================================================= * Call the search, with the document specification and the list of * document ids - the search will never return more than exactly * one match per document id * * This call will return all the metadata that was specified in the * document specification for all the documents and * records in one call. *================================================================*/ searchResults = documentSpecificationSearch(spec, 0, 0, 1, fetchIds.length, fetchIds, categoryPropertyValues); // If we ask for a document and it is no longer there, we should treat this as a deletion. // The activity in that case is to delete the document. A similar thing should happen if // any of the other methods (like getting the document's content) also fail to find the // document. // Let's build a hash which contains all the document metadata returned. The form of // the hash will be: key = the document identifier, value = another hash, which is keyed // by the metadata category/property, and which has a value that is the metadata value. Map<Long,MutableInteger> counterMap = new HashMap<Long,MutableInteger>(); if (searchResults.dsDM != null) { SEARCHRESULTS_DOCUMENTS [] searchResultsDocuments = searchResults.dsDM.getSEARCHRESULTS_DOCUMENTS(); for (SEARCHRESULTS_DOCUMENTS searchResultsDocument : searchResultsDocuments) { long docId = searchResultsDocument.getDocId(); Long docKey = new Long(docId); MutableInteger counterMapItem = counterMap.get(docKey); if (counterMapItem == null) { counterMapItem = new MutableInteger(); counterMap.put(docKey,counterMapItem); } String propertyName = categoryPropertyStringValues[counterMapItem.getValue()]; counterMapItem.increment(); String propertyValue = searchResultsDocuments[i].getStr_value(); Map<String,String> propertyMap = documentPropertyMap.get(docKey); if (propertyMap == null) { propertyMap = new HashMap<String,String>(); documentPropertyMap.put(docKey,propertyMap); } if (propertyValue != null && propertyValue.length() > 0) propertyMap.put(propertyName,propertyValue); } } } // Okay, we are ready now to go through the individual documents and do the ingestion or deletion. for (String documentIdentifier : documentIdentifiers) { Long docKey = new Long(documentIdentifier); long docId = docKey.longValue(); String docVersion = versionStrings.get(docKey); if (docVersion != null) { if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Processing document identifier '" + documentIdentifier + "' " + "with version string '" + docVersion + "'"); // For each document, be sure the job is still allowed to run. activities.checkJobStillActive(); RepositoryDocument repositoryDocument = new RepositoryDocument(); // Load the metadata items into the ingestion document object Map<String,String> docMetadataMap = documentPropertyMap.get(docKey); if (docMetadataMap != null) { for (String categoryPropertyName : categoryPropertyStringValues) { String propertyValue = docMetadataMap.get(categoryPropertyName); if (propertyValue != null && propertyValue.length() > 0) repositoryDocument.addField(categoryPropertyName,propertyValue); } } /*================================================================= * Construct the URL to the object * * HTTP://HOST:PORT/meridio/browse/downloadcontent.aspx?documentId=<docId>&launchMode=1&launchAs=0 * * I expect we need to add additional parameters to the configuration * specification *================================================================*/ String fileURL = urlBase + new Long(docId).toString(); if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("URL for document '" + new Long(docId).toString() + "' is '" + fileURL + "'"); /*================================================================= * Get the object's ACLs and owner information *================================================================*/ DMDataSet documentData = null; documentData = meridio_.getDocumentData((int)docId, true, true, false, false, DmVersionInfo.LATEST, false, false, false); if (null == documentData) { if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Could not retrieve document data for document id '" + new Long(docId).toString() + "' in processDocuments method - deleting document."); activities.noDocument(documentIdentifier,docVersion); continue; } if (null == documentData.getDOCUMENTS() || documentData.getDOCUMENTS().length != 1) { if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Could not retrieve document owner for document id '" + new Long(docId).toString() + "' in processDocuments method. No information or incorrect amount " + "of information was returned"); activities.noDocument(documentIdentifier,docVersion); continue; } // Do path metadata if (pathAttributeName != null && pathAttributeName.length() > 0) { if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Path attribute name is "+pathAttributeName); RMDataSet partList; int recordType = documentData.getDOCUMENTS()[0].getPROP_recordType(); if (recordType == 0 || recordType == 4 || recordType == 19) partList = meridio_.getRecordPartList((int)docId, false, false); else partList = meridio_.getDocumentPartList((int)docId); if (partList != null) { if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Document '"+new Long(docId).toString()+"' has a part list with "+Integer.toString(partList.getRm2vPart().length)+" values"); for (int k = 0; k < partList.getRm2vPart().length; k++) { repositoryDocument.addField(pathAttributeName,matchMap.translate(partList.getRm2vPart()[k].getParentTitlePath())); } } else { if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Document '"+new Long(docId).toString()+"' has no part list, so no path attribute"); } } // Process acls. If there are forced acls, use those, otherwise get them from Meridio. String [] allowAcls; String [] denyAcls; // forcedAcls will be null if security is off, or nonzero length if security is on but hard-wired if (acls != null && acls.length == 0) { ACCESSCONTROL [] documentAcls = documentData.getACCESSCONTROL(); List<String> allowAclsArrayList = new ArrayList<String>(); List<String> denyAclsArrayList = new ArrayList<String>(); // Allow a broken authority to disable all Meridio documents, even if the document is 'wide open', because // Meridio does not permit viewing of the document if the user does not exist (at least, I don't know of a way). denyAclsArrayList.add(defaultAuthorityDenyToken); if (documentAcls != null) { for (int j = 0; j < documentAcls.length; j++) { if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug( "Object Id '" + documentAcls[j].getObjectId() + "' " + "Object Type '" + documentAcls[j].getObjectType() + "' " + "Permission '" + documentAcls[j].getPermission() + "' " + "User Id '" + documentAcls[j].getUserId() + "' " + "Group Id '" + documentAcls[j].getGroupId() + "'"); if (documentAcls[j].getPermission() == 0) // prohibit permission { if (documentAcls[j].getGroupId() > 0) { denyAclsArrayList.add("G" + documentAcls[j].getGroupId()); } else if (documentAcls[j].getUserId() > 0) { denyAclsArrayList.add("U" + documentAcls[j].getUserId()); } } else // read, amend or manage { if (documentAcls[j].getGroupId() > 0) { allowAclsArrayList.add("G" + documentAcls[j].getGroupId()); } else if (documentAcls[j].getUserId() > 0) { allowAclsArrayList.add("U" + documentAcls[j].getUserId()); } } } } DOCUMENTS document = documentData.getDOCUMENTS()[0]; if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Document id '" + new Long(docId).toString() + "' is owned by owner id '" + document.getPROP_ownerId() + "' having the owner name '" + document.getPROP_ownerName() + "' Record Type is '" + document.getPROP_recordType() + "'"); if (document.getPROP_recordType() == 4 || document.getPROP_recordType() == 19) { RMDataSet rmds = meridio_.getRecord((int)docId, false, false, false); Rm2vRecord record = rmds.getRm2vRecord()[0]; if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Record User Id Owner is '" + record.getOwnerID() + "' Record Group Owner Id is '" + record.getGroupOwnerID() + "'"); /*================================================================= * Either a group or a user owns a record, cannot be both and the * group takes priority if it is set *================================================================*/ if (record.getGroupOwnerID() > 0) { allowAclsArrayList.add("G" + record.getGroupOwnerID()); } else if (record.getOwnerID() > 0) { allowAclsArrayList.add("U" + record.getOwnerID()); } } else { allowAclsArrayList.add("U" + document.getPROP_ownerId()); } /*================================================================= * Set up the string arrays and then set the ACLs in the * repository document *================================================================*/ allowAcls = new String[allowAclsArrayList.size()]; for (int j = 0; j < allowAclsArrayList.size(); j++) { allowAcls[j] = allowAclsArrayList.get(j); if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Adding '" + allowAcls[j] + "' to allow ACLs"); } denyAcls = new String[denyAclsArrayList.size()]; for (int j = 0; j < denyAclsArrayList.size(); j++) { denyAcls[j] = denyAclsArrayList.get(j); if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Adding '" + denyAcls[j] + "' to deny ACLs"); } } else { allowAcls = acls; if (allowAcls == null) denyAcls = null; else denyAcls = new String[]{defaultAuthorityDenyToken}; } repositoryDocument.setSecurity(RepositoryDocument.SECURITY_TYPE_DOCUMENT,allowAcls,denyAcls); /*================================================================= * Get the object's content, and ingest the document *================================================================*/ try { AttachmentPart ap = meridio_.getLatestVersionFile((int)docId); if (null == ap) { if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Failed to get content for document '" + new Long(docId).toString() + "'"); // No document. Delete what's there activities.noDocument(documentIdentifier,docVersion); continue; } try { // Get the file name. String fileName = ap.getDataHandler().getName(); // Log what we are about to do. if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: File data is supposedly in "+fileName); File theTempFile = new File(fileName); if (theTempFile.isFile()) { long fileSize = theTempFile.length(); // ap.getSize(); if (activities.checkLengthIndexable(fileSize)) { InputStream is = new FileInputStream(theTempFile); // ap.getDataHandler().getInputStream(); try { repositoryDocument.setBinary(is, fileSize); if (null != activities) { activities.ingestDocumentWithException(documentIdentifier, docVersion, fileURL, repositoryDocument); } } finally { is.close(); } } else { activities.noDocument(documentIdentifier, docVersion); continue; } } else { if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Expected temporary file was not present - skipping document '"+new Long(docId).toString() + "'"); activities.deleteDocument(documentIdentifier); continue; } } finally { ap.dispose(); } } catch (java.net.SocketTimeoutException ioex) { throw new ManifoldCFException("Socket timeout exception: "+ioex.getMessage(), ioex); } catch (ConnectTimeoutException ioex) { throw new ManifoldCFException("Connect timeout exception: "+ioex.getMessage(), ioex); } catch (InterruptedIOException e) { throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED); } catch (org.apache.axis.AxisFault e) { throw e; } catch (RemoteException e) { throw e; } catch (SOAPException soapEx) { throw new ManifoldCFException("SOAP Exception encountered while retrieving document content: "+soapEx.getMessage(), soapEx); } catch (IOException ioex) { throw new ManifoldCFException("Input stream failure: "+ioex.getMessage(), ioex); } } } Logging.connectors.debug("Meridio: Exiting 'processDocuments' method"); return; } catch (org.apache.axis.AxisFault e) { long currentTime = System.currentTimeMillis(); if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://xml.apache.org/axis/","HTTP"))) { org.w3c.dom.Element elem = e.lookupFaultDetail(new javax.xml.namespace.QName("http://xml.apache.org/axis/","HttpErrorCode")); if (elem != null) { elem.normalize(); String httpErrorCode = elem.getFirstChild().getNodeValue().trim(); throw new ManifoldCFException("Unexpected http error code "+httpErrorCode+" accessing Meridio: "+e.getMessage(),e); } throw new ManifoldCFException("Unknown http error occurred while getting doc versions: "+e.getMessage(),e); } if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://schemas.xmlsoap.org/soap/envelope/","Server.userException"))) { String exceptionName = e.getFaultString(); if (exceptionName.equals("java.lang.InterruptedException")) throw new ManifoldCFException("Interrupted",ManifoldCFException.INTERRUPTED); } if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://schemas.xmlsoap.org/soap/envelope/","Server"))) { if (e.getFaultString().indexOf(" 23031#") != -1) { // This means that the session has expired, so reset it and retry meridio_ = null; continue; } } if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Got an unknown remote exception getting doc versions - axis fault = "+e.getFaultCode().getLocalPart()+", detail = "+e.getFaultString()+" - retrying",e); throw new ServiceInterruption("Remote procedure exception: "+e.getMessage(), e, currentTime + 300000L, currentTime + 3 * 60 * 60000L,-1,false); } catch (RemoteException remoteException) { throw new ManifoldCFException("Meridio: A remote exception occurred while getting doc versions: " + remoteException.getMessage(), remoteException); } catch (MeridioDataSetException meridioDataSetException) { throw new ManifoldCFException("Meridio: A problem occurred manipulating the Web " + "Service XML: "+meridioDataSetException.getMessage(), meridioDataSetException); } } } // UI support methods. // // These support methods come in two varieties. The first bunch is involved in setting up connection configuration information. The second bunch // is involved in presenting and editing document specification information for a job. The two kinds of methods are accordingly treated differently, // in that the first bunch cannot assume that the current connector object is connected, while the second bunch can. That is why the first bunch // receives a thread context argument for all UI methods, while the second bunch does not need one (since it has already been applied via the connect() // method, above). /** 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, Locale locale, ConfigParams parameters, List<String> tabsArray) throws ManifoldCFException, IOException { tabsArray.add(Messages.getString(locale,"MeridioConnector.DocumentServer")); tabsArray.add(Messages.getString(locale,"MeridioConnector.RecordsServer")); tabsArray.add(Messages.getString(locale,"MeridioConnector.Credentials")); tabsArray.add(Messages.getString(locale,"MeridioConnector.WebClient")); out.print( "<script type=\"text/javascript\">\n"+ "<!--\n"+ "\n"+ "function checkConfig()\n"+ "{\n"+ " if (editconnection.dmwsServerPort.value != \"\" && !isInteger(editconnection.dmwsServerPort.value))\n"+ " {\n"+ " alert(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.PleaseSupplyAValidNumber") + "\");\n"+ " editconnection.dmwsServerPort.focus();\n"+ " return false;\n"+ " }\n"+ " if (editconnection.rmwsServerPort.value != \"\" && !isInteger(editconnection.rmwsServerPort.value))\n"+ " {\n"+ " alert(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.PleaseSupplyAValidNumber") + "\");\n"+ " editconnection.dmwsServerPort.focus();\n"+ " return false;\n"+ " }\n"+ " if (editconnection.dmwsProxyPort.value != \"\" && !isInteger(editconnection.dmwsProxyPort.value))\n"+ " {\n"+ " alert(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.PleaseSupplyAValidNumber") + "\");\n"+ " editconnection.dmwsProxyPort.focus();\n"+ " return false;\n"+ " }\n"+ " if (editconnection.rmwsProxyPort.value != \"\" && !isInteger(editconnection.rmwsProxyPort.value))\n"+ " {\n"+ " alert(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.PleaseSupplyAValidNumber") + "\");\n"+ " editconnection.dmwsProxyPort.focus();\n"+ " return false;\n"+ " }\n"+ "\n"+ " if (editconnection.webClientServerPort.value != \"\" && !isInteger(editconnection.webClientServerPort.value))\n"+ " {\n"+ " alert(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.PleaseSupplyAValidNumber") + "\");\n"+ " editconnection.webClientServerPort.focus();\n"+ " return false;\n"+ " }\n"+ " if (editconnection.userName.value != \"\" && editconnection.userName.value.indexOf(\"\\\\\") <= 0)\n"+ " {\n"+ " alert(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.AValidMeridioUserNameHasTheForm") + "\");\n"+ " editconnection.userName.focus();\n"+ " return false;\n"+ " }\n"+ " return true;\n"+ "}\n"+ "\n"+ "function checkConfigForSave()\n"+ "{\n"+ " if (editconnection.dmwsServerName.value == \"\")\n"+ " {\n"+ " alert(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.PleaseFillInAMeridioDocumentManagementServerName") + "\");\n"+ " SelectTab(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.DocumentServer") + "\");\n"+ " editconnection.dmwsServerName.focus();\n"+ " return false;\n"+ " }\n"+ " if (editconnection.rmwsServerName.value == \"\")\n"+ " {\n"+ " alert(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.PleaseFillInAMeridioRecordsManagementServerName") + "\");\n"+ " SelectTab(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.RecordsServer") + "\");\n"+ " editconnection.rmwsServerName.focus();\n"+ " return false;\n"+ " }\n"+ " if (editconnection.webClientServerName.value == \"\")\n"+ " {\n"+ " alert(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.PleaseFillInAMeridioWebClientServerName") + "\");\n"+ " SelectTab(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.WebClient") + "\");\n"+ " editconnection.webClientServerName.focus();\n"+ " return false;\n"+ " }\n"+ " if (editconnection.dmwsServerPort.value != \"\" && !isInteger(editconnection.dmwsServerPort.value))\n"+ " {\n"+ " alert(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.PleaseSupplyAMeridioDocumentManagementPortNumber") + "\");\n"+ " SelectTab(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.DocumentServer") + "\");\n"+ " editconnection.dmwsServerPort.focus();\n"+ " return false;\n"+ " }\n"+ " if (editconnection.rmwsServerPort.value != \"\" && !isInteger(editconnection.rmwsServerPort.value))\n"+ " {\n"+ " alert(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.PleaseSupplyAMeridioDocumentManagementPortNumber") + "\");\n"+ " SelectTab(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.RecordsServer") + "\");\n"+ " editconnection.rmwsServerPort.focus();\n"+ " return false;\n"+ " }\n"+ " if (editconnection.webClientServerPort.value != \"\" && !isInteger(editconnection.webClientServerPort.value))\n"+ " {\n"+ " alert(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.PleaseSupplyAMeridioWebClientPortNumberOrNoneForDefault") + "\");\n"+ " SelectTab(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.WebClient") + "\");\n"+ " editconnection.webClientServerPort.focus();\n"+ " return false;\n"+ " }\n"+ " if (editconnection.userName.value == \"\" || editconnection.userName.value.indexOf(\"\\\\\") <= 0)\n"+ " {\n"+ " alert(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.TheConnectionRequiresAValidMeridioUserNameOfTheForm") + "\");\n"+ " SelectTab(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.Credentials") + "\");\n"+ " editconnection.userName.focus();\n"+ " return false;\n"+ " }\n"+ "\n"+ " return true;\n"+ "}\n"+ "\n"+ "function DeleteCertificate(aliasName)\n"+ "{\n"+ " editconnection.keystorealias.value = aliasName;\n"+ " editconnection.configop.value = \"Delete\";\n"+ " postForm();\n"+ "}\n"+ "\n"+ "function AddCertificate()\n"+ "{\n"+ " if (editconnection.certificate.value == \"\")\n"+ " {\n"+ " alert(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.ChooseACertificateFile") + "\");\n"+ " editconnection.certificate.focus();\n"+ " }\n"+ " else\n"+ " {\n"+ " editconnection.configop.value = \"Add\";\n"+ " postForm();\n"+ " }\n"+ "}\n"+ "\n"+ "//-->\n"+ "</script>\n" ); } /** Output the configuration body section. * This method is called in the body section of the connector's configuration page. 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 "editconnection". *@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 tabName is the current tab name. */ @Override public void outputConfigurationBody(IThreadContext threadContext, IHTTPOutput out, Locale locale, ConfigParams parameters, String tabName) throws ManifoldCFException, IOException { String dmwsServerProtocol = parameters.getParameter("DMWSServerProtocol"); if (dmwsServerProtocol == null) dmwsServerProtocol = "http"; String rmwsServerProtocol = parameters.getParameter("RMWSServerProtocol"); if (rmwsServerProtocol == null) rmwsServerProtocol = "http"; String dmwsServerName = parameters.getParameter("DMWSServerName"); if (dmwsServerName == null) dmwsServerName = ""; String rmwsServerName = parameters.getParameter("RMWSServerName"); if (rmwsServerName == null) rmwsServerName = ""; String dmwsServerPort = parameters.getParameter("DMWSServerPort"); if (dmwsServerPort == null) dmwsServerPort = ""; String rmwsServerPort = parameters.getParameter("RMWSServerPort"); if (rmwsServerPort == null) rmwsServerPort = ""; String dmwsLocation = parameters.getParameter("DMWSLocation"); if (dmwsLocation == null) dmwsLocation = "/DMWS/MeridioDMWS.asmx"; String rmwsLocation = parameters.getParameter("RMWSLocation"); if (rmwsLocation == null) rmwsLocation = "/RMWS/MeridioRMWS.asmx"; String dmwsProxyHost = parameters.getParameter("DMWSProxyHost"); if (dmwsProxyHost == null) dmwsProxyHost = ""; String rmwsProxyHost = parameters.getParameter("RMWSProxyHost"); if (rmwsProxyHost == null) rmwsProxyHost = ""; String dmwsProxyPort = parameters.getParameter("DMWSProxyPort"); if (dmwsProxyPort == null) dmwsProxyPort = ""; String rmwsProxyPort = parameters.getParameter("RMWSProxyPort"); if (rmwsProxyPort == null) rmwsProxyPort = ""; String userName = parameters.getParameter("UserName"); if (userName == null) userName = ""; String password = parameters.getObfuscatedParameter("Password"); if (password == null) password = ""; else password = out.mapPasswordToKey(password); String webClientProtocol = parameters.getParameter("MeridioWebClientProtocol"); if (webClientProtocol == null) webClientProtocol = "http"; String webClientServerName = parameters.getParameter("MeridioWebClientServerName"); if (webClientServerName == null) webClientServerName = ""; String webClientServerPort = parameters.getParameter("MeridioWebClientServerPort"); if (webClientServerPort == null) webClientServerPort = ""; String webClientDocDownloadLocation = parameters.getParameter("MeridioWebClientDocDownloadLocation"); if (webClientDocDownloadLocation == null) webClientDocDownloadLocation = "/meridio/browse/downloadcontent.aspx"; String meridioKeystore = parameters.getParameter("MeridioKeystore"); IKeystoreManager localKeystore; if (meridioKeystore == null) localKeystore = KeystoreManagerFactory.make(""); else localKeystore = KeystoreManagerFactory.make("",meridioKeystore); out.print( "<input name=\"configop\" type=\"hidden\" value=\"Continue\"/>\n" ); // "Document Server" tab if (tabName.equals(Messages.getString(locale,"MeridioConnector.DocumentServer"))) { out.print( "<table class=\"displaytable\">\n"+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.DocumentWebserviceServerProtocol") + "</nobr></td>\n"+ " <td class=\"value\">\n"+ " <select name=\"dmwsServerProtocol\">\n"+ " <option value=\"http\" "+((dmwsServerProtocol.equals("http"))?"selected=\"true\"":"")+">http</option>\n"+ " <option value=\"https\" "+(dmwsServerProtocol.equals("https")?"selected=\"true\"":"")+">https</option>\n"+ " </select>\n"+ " </td>\n"+ " </tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.DocumentWebserviceServerName") + "</nobr></td>\n"+ " <td class=\"value\"><input type=\"text\" size=\"64\" name=\"dmwsServerName\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(dmwsServerName)+"\"/></td>\n"+ " </tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.DocumentWebserviceServerPort") + "</nobr></td>\n"+ " <td class=\"value\"><input type=\"text\" size=\"5\" name=\"dmwsServerPort\" value=\""+dmwsServerPort+"\"/></td>\n"+ " </tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.DocumentWebserviceLocation") + "</nobr></td>\n"+ " <td class=\"value\"><input type=\"text\" size=\"64\" name=\"dmwsLocation\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(dmwsLocation)+"\"/></td>\n"+ " </tr>\n"+ " <tr>\n"+ " <td class=\"separator\" colspan=\"2\"><hr/></td>\n"+ " </tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.DocumentWebserviceServerProxyHost") + "</nobr></td>\n"+ " <td class=\"value\"><input type=\"text\" size=\"64\" name=\"dmwsProxyHost\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(dmwsProxyHost)+"\"/></td>\n"+ " </tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.DocumentWebserviceServerProxyPort") + "</nobr></td>\n"+ " <td class=\"value\"><input type=\"text\" size=\"5\" name=\"dmwsProxyPort\" value=\""+dmwsProxyPort+"\"/></td>\n"+ " </tr>\n"+ "</table>\n" ); } else { // Hiddens for the Document Server tab. out.print( "<input type=\"hidden\" name=\"dmwsServerProtocol\" value=\""+dmwsServerProtocol+"\"/>\n"+ "<input type=\"hidden\" name=\"dmwsServerName\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(dmwsServerName)+"\"/>\n"+ "<input type=\"hidden\" name=\"dmwsServerPort\" value=\""+dmwsServerPort+"\"/>\n"+ "<input type=\"hidden\" name=\"dmwsLocation\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(dmwsLocation)+"\"/>\n"+ "<input type=\"hidden\" name=\"dmwsProxyHost\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(dmwsProxyHost)+"\"/>\n"+ "<input type=\"hidden\" name=\"dmwsProxyPort\" value=\""+dmwsProxyPort+"\"/>\n" ); } // "Records Server" tab if (tabName.equals(Messages.getString(locale,"MeridioConnector.RecordsServer"))) { out.print( "<table class=\"displaytable\">\n"+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.RecordWebserviceServerProtocol") + "</nobr></td>\n"+ " <td class=\"value\">\n"+ " <select name=\"rmwsServerProtocol\">\n"+ " <option value=\"http\" "+((rmwsServerProtocol.equals("http"))?"selected=\"true\"":"")+">http</option>\n"+ " <option value=\"https\" "+(rmwsServerProtocol.equals("https")?"selected=\"true\"":"")+">https</option>\n"+ " </select>\n"+ " </td>\n"+ " </tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.RecordWebserviceServerName") + "</nobr></td>\n"+ " <td class=\"value\"><input type=\"text\" size=\"64\" name=\"rmwsServerName\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(rmwsServerName)+"\"/></td>\n"+ " </tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.RecordWebserviceServerPort") + "</nobr></td>\n"+ " <td class=\"value\"><input type=\"text\" size=\"5\" name=\"rmwsServerPort\" value=\""+rmwsServerPort+"\"/></td>\n"+ " </tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.RecordWebserviceLocation") + "</nobr></td>\n"+ " <td class=\"value\"><input type=\"text\" size=\"64\" name=\"rmwsLocation\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(rmwsLocation)+"\"/></td>\n"+ " </tr>\n"+ " <tr>\n"+ " <td class=\"separator\" colspan=\"2\"><hr/></td>\n"+ " </tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.RecordWebserviceServerProxyHost") + "</nobr></td>\n"+ " <td class=\"value\"><input type=\"text\" size=\"64\" name=\"rmwsProxyHost\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(rmwsProxyHost)+"\"/></td>\n"+ " </tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.RecordWebserviceServerProxyPort") + "</nobr></td>\n"+ " <td class=\"value\"><input type=\"text\" size=\"5\" name=\"rmwsProxyPort\" value=\""+rmwsProxyPort+"\"/></td>\n"+ " </tr>\n"+ "</table>\n" ); } else { // Hiddens for the Records Server tab. out.print( "<input type=\"hidden\" name=\"rmwsServerProtocol\" value=\""+rmwsServerProtocol+"\"/>\n"+ "<input type=\"hidden\" name=\"rmwsServerName\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(rmwsServerName)+"\"/>\n"+ "<input type=\"hidden\" name=\"rmwsServerPort\" value=\""+rmwsServerPort+"\"/>\n"+ "<input type=\"hidden\" name=\"rmwsLocation\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(rmwsLocation)+"\"/>\n"+ "<input type=\"hidden\" name=\"rmwsProxyHost\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(rmwsProxyHost)+"\"/>\n"+ "<input type=\"hidden\" name=\"rmwsProxyPort\" value=\""+rmwsProxyPort+"\"/>\n" ); } // The "Credentials" tab // Always pass the whole keystore as a hidden. if (meridioKeystore != null) { out.print( "<input type=\"hidden\" name=\"keystoredata\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(meridioKeystore)+"\"/>\n" ); } if (tabName.equals(Messages.getString(locale,"MeridioConnector.Credentials"))) { out.print( "<table class=\"displaytable\">\n"+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.UserName") + "</nobr></td>\n"+ " <td class=\"value\"><input type=\"text\" size=\"32\" name=\"userName\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(userName)+"\"/></td>\n"+ " </tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.Password") + "</nobr></td>\n"+ " <td class=\"value\"><input type=\"password\" size=\"32\" name=\"password\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(password)+"\"/></td>\n"+ " </tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.SSLCertificateList") + "</nobr></td>\n"+ " <td class=\"value\">\n"+ " <input type=\"hidden\" name=\"keystorealias\" value=\"\"/>\n"+ " <table class=\"displaytable\">\n" ); // List the individual certificates in the store, with a delete button for each String[] contents = localKeystore.getContents(); if (contents.length == 0) { out.print( " <tr><td class=\"message\" colspan=\"2\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.NoCertificatesPresent") + "</nobr></td></tr>\n" ); } else { int i = 0; while (i < contents.length) { String alias = contents[i]; String description = localKeystore.getDescription(alias); if (description.length() > 128) description = description.substring(0,125) + "..."; out.print( " <tr>\n"+ " <td class=\"value\"><input type=\"button\" onclick='Javascript:DeleteCertificate(\""+org.apache.manifoldcf.ui.util.Encoder.attributeJavascriptEscape(alias)+"\")' alt=\""+Messages.getAttributeString(locale,"MeridioConnector.DeleteCert")+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(alias)+"\" value=\"Delete\"/></td>\n"+ " <td>"+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(description)+"</td>\n"+ " </tr>\n" ); i++; } } out.print( " </table>\n"+ " <input type=\"button\" onclick='Javascript:AddCertificate()' alt=\"" + Messages.getAttributeString(locale,"MeridioConnector.AddCert") + "\" value=\"Add\"/> \n"+ " Certificate: <input name=\"certificate\" size=\"50\" type=\"file\"/>\n"+ " </td>\n"+ " </tr>\n"+ "</table>\n" ); } else { // Hiddens for the "Credentials" tab out.print( "<input type=\"hidden\" name=\"userName\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(userName)+"\"/>\n"+ "<input type=\"hidden\" name=\"password\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(password)+"\"/>\n" ); } // Web Client tab if (tabName.equals(Messages.getString(locale,"MeridioConnector.WebClient"))) { out.print( "<table class=\"displaytable\">\n"+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.WebClientProtocol") + "</nobr></td>\n"+ " <td class=\"value\">\n"+ " <select name=\"webClientProtocol\">\n"+ " <option value=\"http\" "+((webClientProtocol.equals("http"))?"selected=\"true\"":"")+">http</option>\n"+ " <option value=\"https\" "+(webClientProtocol.equals("https")?"selected=\"true\"":"")+">https</option>\n"+ " </select>\n"+ " </td>\n"+ " </tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.WebClientServerName") + "</nobr></td>\n"+ " <td class=\"value\"><input type=\"text\" size=\"64\" name=\"webClientServerName\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(webClientServerName)+"\"/></td>\n"+ " </tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.WebClientServerPort") + "</nobr></td>\n"+ " <td class=\"value\"><input type=\"text\" size=\"5\" name=\"webClientServerPort\" value=\""+webClientServerPort+"\"/></td>\n"+ " </tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.WebClientServerDocLocation") + "</nobr></td>\n"+ " <td class=\"value\"><input type=\"text\" size=\"64\" name=\"webClientDocDownloadLocation\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(webClientDocDownloadLocation)+"\"/></td>\n"+ " </tr>\n"+ "</table>\n" ); } else { // Hiddens for the "Web Client" tab out.print( "<input type=\"hidden\" name=\"webClientProtocol\" value=\""+webClientProtocol+"\"/>\n"+ "<input type=\"hidden\" name=\"webClientServerName\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(webClientServerName)+"\"/>\n"+ "<input type=\"hidden\" name=\"webClientServerPort\" value=\""+webClientServerPort+"\"/>\n"+ "<input type=\"hidden\" name=\"webClientDocDownloadLocation\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(webClientDocDownloadLocation)+"\"/>\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, Locale locale, ConfigParams parameters) throws ManifoldCFException { String dmwsServerProtocol = variableContext.getParameter("dmwsServerProtocol"); if (dmwsServerProtocol != null) parameters.setParameter("DMWSServerProtocol",dmwsServerProtocol); String rmwsServerProtocol = variableContext.getParameter("rmwsServerProtocol"); if (rmwsServerProtocol != null) parameters.setParameter("RMWSServerProtocol",rmwsServerProtocol); String dmwsServerName = variableContext.getParameter("dmwsServerName"); if (dmwsServerName != null) parameters.setParameter("DMWSServerName",dmwsServerName); String rmwsServerName = variableContext.getParameter("rmwsServerName"); if (rmwsServerName != null) parameters.setParameter("RMWSServerName",rmwsServerName); String dmwsServerPort = variableContext.getParameter("dmwsServerPort"); if (dmwsServerPort != null) { if (dmwsServerPort.length() > 0) parameters.setParameter("DMWSServerPort",dmwsServerPort); else parameters.setParameter("DMWSServerPort",null); } String rmwsServerPort = variableContext.getParameter("rmwsServerPort"); if (rmwsServerPort != null) { if (rmwsServerPort.length() > 0) parameters.setParameter("RMWSServerPort",rmwsServerPort); else parameters.setParameter("RMWSServerPort",null); } String dmwsLocation = variableContext.getParameter("dmwsLocation"); if (dmwsLocation != null) parameters.setParameter("DMWSLocation",dmwsLocation); String rmwsLocation = variableContext.getParameter("rmwsLocation"); if (rmwsLocation != null) parameters.setParameter("RMWSLocation",rmwsLocation); String dmwsProxyHost = variableContext.getParameter("dmwsProxyHost"); if (dmwsProxyHost != null) parameters.setParameter("DMWSProxyHost",dmwsProxyHost); String rmwsProxyHost = variableContext.getParameter("rmwsProxyHost"); if (rmwsProxyHost != null) parameters.setParameter("RMWSProxyHost",rmwsProxyHost); String dmwsProxyPort = variableContext.getParameter("dmwsProxyPort"); if (dmwsProxyPort != null && dmwsProxyPort.length() > 0) parameters.setParameter("DMWSProxyPort",dmwsProxyPort); String rmwsProxyPort = variableContext.getParameter("rmwsProxyPort"); if (rmwsProxyPort != null && rmwsProxyPort.length() > 0) parameters.setParameter("RMWSProxyPort",rmwsProxyPort); String userName = variableContext.getParameter("userName"); if (userName != null) parameters.setParameter("UserName",userName); String password = variableContext.getParameter("password"); if (password != null) parameters.setObfuscatedParameter("Password",variableContext.mapKeyToPassword(password)); String webClientProtocol = variableContext.getParameter("webClientProtocol"); if (webClientProtocol != null) parameters.setParameter("MeridioWebClientProtocol",webClientProtocol); String webClientServerName = variableContext.getParameter("webClientServerName"); if (webClientServerName != null) parameters.setParameter("MeridioWebClientServerName",webClientServerName); String webClientServerPort = variableContext.getParameter("webClientServerPort"); if (webClientServerPort != null) { if (webClientServerPort.length() > 0) parameters.setParameter("MeridioWebClientServerPort",webClientServerPort); else parameters.setParameter("MeridioWebClientServerPort",null); } String webClientDocDownloadLocation = variableContext.getParameter("webClientDocDownloadLocation"); if (webClientDocDownloadLocation != null) parameters.setParameter("MeridioWebClientDocDownloadLocation",webClientDocDownloadLocation); String configOp = variableContext.getParameter("configop"); if (configOp != null) { String keystoreValue; if (configOp.equals("Delete")) { String alias = variableContext.getParameter("keystorealias"); keystoreValue = parameters.getParameter("MeridioKeystore"); IKeystoreManager mgr; if (keystoreValue != null) mgr = KeystoreManagerFactory.make("",keystoreValue); else mgr = KeystoreManagerFactory.make(""); mgr.remove(alias); parameters.setParameter("MeridioKeystore",mgr.getString()); } else if (configOp.equals("Add")) { String alias = IDFactory.make(threadContext); byte[] certificateValue = variableContext.getBinaryBytes("certificate"); keystoreValue = parameters.getParameter("MeridioKeystore"); IKeystoreManager mgr; if (keystoreValue != null) mgr = KeystoreManagerFactory.make("",keystoreValue); else mgr = KeystoreManagerFactory.make(""); java.io.InputStream is = new java.io.ByteArrayInputStream(certificateValue); String certError = null; try { mgr.importCertificate(alias,is); } catch (Throwable e) { certError = e.getMessage(); } finally { try { is.close(); } catch (IOException e) { // Eat this exception } } if (certError != null) { // Redirect to error page return "Illegal certificate: "+certError; } parameters.setParameter("MeridioKeystore",mgr.getString()); } } return null; } /** 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, Locale locale, ConfigParams parameters) throws ManifoldCFException, IOException { out.print( "<table class=\"displaytable\">\n"+ " <tr>\n"+ " <td class=\"description\" colspan=\"1\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.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" ); } // The allowed mime types, which are those that the ingestion API understands private static String[] allowedMimeTypes; static { allowedMimeTypes = new String[] { "application/excel", "application/powerpoint", "application/ppt", "application/rtf", "application/xls", "text/html", "text/rtf", "text/pdf", "application/x-excel", "application/x-msexcel", "application/x-mspowerpoint", "application/x-msword-doc", "application/x-msword", "application/x-word", "Application/pdf", "text/xml", "no-type", "text/plain", "application/pdf", "application/x-rtf", "application/vnd.ms-excel", "application/vnd.ms-pps", "application/vnd.ms-powerpoint", "application/vnd.ms-word", "application/msword", "application/msexcel", "application/mspowerpoint", "application/ms-powerpoint", "application/ms-word", "application/ms-excel", "Adobe", "application/Vnd.Ms-Excel", "vnd.ms-powerpoint", "application/x-pdf", "winword", "text/richtext", "Text", "Text/html", "application/MSWORD", "application/PDF", "application/MSEXCEL", "application/MSPOWERPOINT" }; java.util.Arrays.sort(allowedMimeTypes); } /** 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. * The connector will be connected before this method can be called. *@param out is the output to which any HTML should be sent. *@param locale is the locale the output is preferred to be in. *@param ds is the current document specification for this job. *@param connectionSequenceNumber is the unique number of this connection within the 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, Locale locale, Specification ds, int connectionSequenceNumber, List<String> tabsArray) throws ManifoldCFException, IOException { tabsArray.add(Messages.getString(locale,"MeridioConnector.SearchPaths")); tabsArray.add(Messages.getString(locale,"MeridioConnector.ContentTypes")); tabsArray.add(Messages.getString(locale,"MeridioConnector.Categories")); tabsArray.add(Messages.getString(locale,"MeridioConnector.DataTypes")); tabsArray.add(Messages.getString(locale,"MeridioConnector.Security")); tabsArray.add(Messages.getString(locale,"MeridioConnector.Metadata")); String seqPrefix = "s"+connectionSequenceNumber+"_"; out.print( "<script type=\"text/javascript\">\n"+ "<!--\n"+ "\n"+ "function "+seqPrefix+"SpecDeletePath(n)\n"+ "{\n"+ " var anchor;\n"+ " if (n == 0)\n"+ " anchor = \""+seqPrefix+"SpecPathAdd\";\n"+ " else\n"+ " anchor = \""+seqPrefix+"SpecPath_\"+(n-1);\n"+ " "+seqPrefix+"SpecOp(\""+seqPrefix+"specpathop_\"+n,\"Delete\",anchor);\n"+ "}\n"+ "\n"+ "function "+seqPrefix+"SpecAddPath()\n"+ "{\n"+ " "+seqPrefix+"SpecOp(\""+seqPrefix+"specpathop\",\"Add\",\""+seqPrefix+"SpecPathAdd\");\n"+ "}\n"+ "\n"+ "function "+seqPrefix+"SpecDeleteFromPath()\n"+ "{\n"+ " "+seqPrefix+"SpecOp(\""+seqPrefix+"specpathop\",\"DeleteFromPath\",\""+seqPrefix+"SpecPathAdd\");\n"+ "}\n"+ "\n"+ "function "+seqPrefix+"SpecAddToPath()\n"+ "{\n"+ " if (editjob."+seqPrefix+"specpath.value == \"\")\n"+ " {\n"+ " alert(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.SelectAFolderOrClassFirst") + "\");\n"+ " editjob."+seqPrefix+"specpath.focus();\n"+ " }\n"+ " else\n"+ " "+seqPrefix+"SpecOp(\""+seqPrefix+"specpathop\",\"AddToPath\",\""+seqPrefix+"SpecPathAdd\");\n"+ "}\n"+ "\n"+ "function "+seqPrefix+"SpecAddAccessToken(anchorvalue)\n"+ "{\n"+ " if (editjob."+seqPrefix+"spectoken.value == \"\")\n"+ " {\n"+ " alert(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.AccessTokenCannotBeNull") + "\");\n"+ " editjob."+seqPrefix+"spectoken.focus();\n"+ " }\n"+ " else\n"+ " "+seqPrefix+"SpecOp(\""+seqPrefix+"accessop\",\"Add\",anchorvalue);\n"+ "}\n"+ "\n"+ "function "+seqPrefix+"SpecDeleteMapping(item, anchorvalue)\n"+ "{\n"+ " "+seqPrefix+"SpecOp(\""+seqPrefix+"specmappingop_\"+item,\"Delete\",anchorvalue);\n"+ "}\n"+ "\n"+ "function "+seqPrefix+"SpecAddMapping(anchorvalue)\n"+ "{\n"+ " if (editjob."+seqPrefix+"specmatch.value == \"\")\n"+ " {\n"+ " alert(\"" + Messages.getBodyJavascriptString(locale,"MeridioConnector.MatchStringCannotBeEmpty") + "\");\n"+ " editjob."+seqPrefix+"specmatch.focus();\n"+ " return;\n"+ " }\n"+ " "+seqPrefix+"SpecOp(\""+seqPrefix+"specmappingop\",\"Add\",anchorvalue);\n"+ "}\n"+ "\n"+ "function "+seqPrefix+"SpecOp(n, opValue, anchorvalue)\n"+ "{\n"+ " eval(\"editjob.\"+n+\".value = \\\"\"+opValue+\"\\\"\");\n"+ " postFormSetAnchor(anchorvalue);\n"+ "}\n"+ "//-->\n"+ "</script>\n" ); } /** 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 always "editjob". * The connector will be connected before this method can be called. *@param out is the output to which any HTML should be sent. *@param locale is the locale the output is preferred to be in. *@param ds is the current document specification for this job. *@param connectionSequenceNumber is the unique number of this connection within the job. *@param actualSequenceNumber is the connection within the job that has currently been selected. *@param tabName is the current tab name. (actualSequenceNumber, tabName) form a unique tuple within * the job. */ @Override public void outputSpecificationBody(IHTTPOutput out, Locale locale, Specification ds, int connectionSequenceNumber, int actualSequenceNumber, String tabName) throws ManifoldCFException, IOException { String seqPrefix = "s"+connectionSequenceNumber+"_"; int i; int k; // Search Paths tab if (tabName.equals(Messages.getString(locale,"MeridioConnector.SearchPaths")) && connectionSequenceNumber == actualSequenceNumber) { out.print( "<table class=\"displaytable\">\n"+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n" ); i = 0; k = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("SearchPath")) { // Found a search path. Not clear from the spec what the attribute is, or whether this is // body data, so I'm going to presume it's a path attribute. String pathString = sn.getAttributeValue("path"); out.print( " <tr>\n"+ " <td class=\"description\">\n"+ " <input type=\"hidden\" name=\""+seqPrefix+"specpathop_"+Integer.toString(k)+"\" value=\"Continue\"/>\n"+ " <input type=\"hidden\" name=\""+seqPrefix+"specpath_"+Integer.toString(k)+"\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(pathString)+"\"/>\n"+ " <a name=\""+seqPrefix+"SpecPath_"+Integer.toString(k)+"\">\n"+ " <input type=\"button\" value=\"Delete\" onclick='javascript:"+seqPrefix+"SpecDeletePath("+Integer.toString(k)+");' alt=\""+Messages.getAttributeString(locale,"MeridioConnector.DeletePath")+Integer.toString(k)+"\"/>\n"+ " </a>\n"+ " </td>\n"+ " <td class=\"value\"><nobr>"+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(pathString)+"</nobr></td>\n"+ " </tr>\n" ); k++; } } if (k == 0) { out.print( " <tr><td class=\"message\" colspan=\"2\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.NoPathsSpecified") + "</nobr></td></tr>\n" ); } out.print( " <tr>\n"+ " <td class=\"lightseparator\" colspan=\"2\"><input type=\"hidden\" name=\""+seqPrefix+"specpath_total\" value=\""+Integer.toString(k)+"\"/><hr/></td>\n"+ " </tr>\n"+ " <tr>\n" ); // The path, and the corresponding IDs String pathSoFar = (String)currentContext.get(seqPrefix+"specpath"); String idsSoFar = (String)currentContext.get(seqPrefix+"specpathids"); // The type of the object described by the path Integer containerType = (Integer)currentContext.get(seqPrefix+"specpathtype"); if (pathSoFar == null) pathSoFar = "/"; if (idsSoFar == null) idsSoFar = "0"; if (containerType == null) containerType = new Integer(org.apache.manifoldcf.crawler.connectors.meridio.MeridioClassContents.CLASS); int currentInt = 0; if (idsSoFar.length() > 0) { String[] ids = idsSoFar.split(","); currentInt = Integer.parseInt(ids[ids.length-1]); } // Grab next folder/project list try { org.apache.manifoldcf.crawler.connectors.meridio.MeridioClassContents[] childList; if (containerType.intValue() == org.apache.manifoldcf.crawler.connectors.meridio.MeridioClassContents.CLASS) { childList = getClassOrFolderContents(currentInt); } else childList = new org.apache.manifoldcf.crawler.connectors.meridio.MeridioClassContents[0]; out.print( " <td class=\"description\">\n"+ " <input type=\"hidden\" name=\""+seqPrefix+"specpathop\" value=\"Continue\"/>\n"+ " <input type=\"hidden\" name=\""+seqPrefix+"specpathbase\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(pathSoFar)+"\"/>\n"+ " <input type=\"hidden\" name=\""+seqPrefix+"specidsbase\" value=\""+idsSoFar+"\"/>\n"+ " <input type=\"hidden\" name=\""+seqPrefix+"spectype\" value=\""+containerType.toString()+"\"/>\n"+ " <a name=\""+seqPrefix+"SpecPathAdd\"><input type=\"button\" value=\"Add\" onclick=\"javascript:"+seqPrefix+"SpecAddPath();\" alt=\"" + Messages.getAttributeString(locale,"MeridioConnector.AddPath") + "\"/></a>\n"+ " </td>\n"+ " <td class=\"value\">\n"+ " <nobr>\n"+ " "+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(pathSoFar)+"\n" ); if (pathSoFar.length() > 1) { out.print( " <input type=\"button\" value=\"-\" onclick=\"javascript:"+seqPrefix+"SpecDeleteFromPath();\" alt=\"" + Messages.getAttributeString(locale,"MeridioConnector.DeleteFromPath") + "\"/>\n" ); } if (childList.length > 0) { out.print( " <input type=\"button\" value=\"+\" onclick=\"javascript:"+seqPrefix+"SpecAddToPath();\" alt=\"" + Messages.getAttributeString(locale,"MeridioConnector.AddToPath") + "\"/>\n"+ " <select name=\""+seqPrefix+"specpath\" size=\"10\">\n"+ " <option value=\"\" selected=\"\">" + Messages.getBodyString(locale,"MeridioConnector.PickAFolder") + "</option>\n" ); int j = 0; while (j < childList.length) { // The option selected needs to include both the id and the name, since I have no way // to get to the name from the id. So, put the id first, then a semicolon, then the name. out.print( " <option value=\""+Integer.toString(childList[j].classOrFolderId)+";"+Integer.toString(childList[j].containerType)+";"+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(childList[j].classOrFolderName)+"\">\n"+ " "+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(childList[j].classOrFolderName)+"\n"+ " </option>\n" ); j++; } out.print( " </select>\n" ); } out.print( " </nobr>\n"+ " </td>\n" ); } catch (ServiceInterruption e) { e.printStackTrace(); out.print( " <td class=\"message\" colspan=\"2\">" + Messages.getBodyString(locale,"MeridioConnector.ServiceInterruption") +org.apache.manifoldcf.ui.util.Encoder.bodyEscape(e.getMessage())+"</td>\n" ); } catch (ManifoldCFException e) { e.printStackTrace(); out.print( " <td class=\"message\" colspan=\"2\">"+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(e.getMessage())+"</td>\n" ); } out.print( " </tr>\n"+ "</table>\n" ); } else { // The path tab is hidden; just preserve the contents i = 0; k = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("SearchPath")) { // Found a search path. Not clear from the spec what the attribute is, or whether this is // body data, so I'm going to presume it's a value attribute. String pathString = sn.getAttributeValue("path"); out.print( "<input type=\"hidden\" name=\""+seqPrefix+"specpath_"+Integer.toString(k)+"\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(pathString)+"\"/>\n" ); k++; } } out.print( "<input type=\"hidden\" name=\""+seqPrefix+"specpath_total\" value=\""+Integer.toString(k)+"\"/>\n" ); } // Content Types tab Set<String> mimeTypeMap = new HashSet<String>(); for (i = 0; i < ds.getChildCount(); i++) { SpecificationNode sn = ds.getChild(i); if (sn.getType().equals("MIMEType")) { String type = sn.getAttributeValue("type"); mimeTypeMap.add(type); } } // If there are none selected, then check them all, since no mime types would be nonsensical. if (mimeTypeMap.size() == 0) { for (String allowedMimeType : allowedMimeTypes) { mimeTypeMap.add(allowedMimeType); } } if (tabName.equals(Messages.getString(locale,"MeridioConnector.ContentTypes")) && connectionSequenceNumber == actualSequenceNumber) { out.print( "<table class=\"displaytable\">\n"+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.MimeTypes") + "</nobr></td>\n"+ " <td class=\"value\">\n" ); i = 0; while (i < allowedMimeTypes.length) { String mimeType = allowedMimeTypes[i++]; out.print( " <nobr>\n"+ " <input type=\"checkbox\" name=\""+seqPrefix+"specmimetypes\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(mimeType)+"\" "+(mimeTypeMap.contains(mimeType)?"checked=\"true\"":"")+">\n"+ " "+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(mimeType)+"\n"+ " </input>\n"+ " </nobr>\n"+ " <br/>\n" ); } out.print( " </td>\n"+ " </tr>\n"+ "</table>\n" ); } else { // Tab is not selected. Submit a separate hidden for each value that was selected before. Iterator<String> iter = mimeTypeMap.iterator(); while (iter.hasNext()) { String mimeType = iter.next(); out.print( "<input type=\"hidden\" name=\""+seqPrefix+"specmimetypes\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(mimeType)+"\"/>\n" ); } } // Categories tab Set<String> categoriesMap = new HashSet<String>(); for (i = 0; i < ds.getChildCount(); i++) { SpecificationNode sn = ds.getChild(i); if (sn.getType().equals("SearchCategory")) { String category = sn.getAttributeValue("category"); categoriesMap.add(category); } } if (tabName.equals(Messages.getString(locale,"MeridioConnector.Categories")) && connectionSequenceNumber == actualSequenceNumber) { out.print( "<table class=\"displaytable\">\n"+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+ " <tr>\n" ); // Grab the list of available categories from Meridio try { String[] categoryList; categoryList = getMeridioCategories(); out.print( " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.Categories") + "</nobr></td>\n"+ " <td class=\"value\">\n" ); k = 0; while (k < categoryList.length) { String category = categoryList[k++]; out.print( " <nobr>\n"+ " <input type=\"checkbox\" name=\""+seqPrefix+"speccategories\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(category)+"\" "+(categoriesMap.contains(category)?"checked=\"true\"":"")+">\n"+ " "+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(category)+"\n"+ " </input>\n"+ " </nobr>\n"+ " <br/>\n" ); } out.print( " </td>\n" ); } catch (ServiceInterruption e) { e.printStackTrace(); out.print( " <td class=\"message\" colspan=\"2\">" + Messages.getBodyString(locale,"MeridioConnector.ServiceInterruption") +org.apache.manifoldcf.ui.util.Encoder.bodyEscape(e.getMessage())+"</td>\n" ); } catch (ManifoldCFException e) { e.printStackTrace(); out.print( " <td class=\"message\" colspan=\"2\">"+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(e.getMessage())+"</td>\n" ); } out.print( " </tr>\n"+ "</table>\n" ); } else { // Tab is not selected. Submit a separate hidden for each value that was selected before. Iterator<String> iter = categoriesMap.iterator(); while (iter.hasNext()) { String category = iter.next(); out.print( "<input type=\"hidden\" name=\""+seqPrefix+"speccategories\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(category)+"\"/>\n" ); } } // Data Types tab String mode = "DOCUMENTS_AND_RECORDS"; i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("SearchOn")) mode = sn.getAttributeValue("value"); } if (tabName.equals(Messages.getString(locale,"MeridioConnector.DataTypes")) && connectionSequenceNumber == actualSequenceNumber) { out.print( "<table class=\"displaytable\">\n"+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.DataTypesToIngest") + "</nobr></td>\n"+ " <td class=\"value\">\n"+ " <nobr><input type=\"radio\" name=\""+seqPrefix+"specsearchon\" value=\"DOCUMENTS\" "+(mode.equals("DOCUMENTS")?"checked=\"true\"":"")+"/>" + Messages.getBodyString(locale,"MeridioConnector.Documents") + "</nobr><br/>\n"+ " <nobr><input type=\"radio\" name=\""+seqPrefix+"specsearchon\" value=\"RECORDS\" "+(mode.equals("RECORDS")?"checked=\"true\"":"")+"/>" + Messages.getBodyString(locale,"MeridioConnector.Records") + "</nobr><br/>\n"+ " <nobr><input type=\"radio\" name=\""+seqPrefix+"specsearchon\" value=\"DOCUMENTS_AND_RECORDS\" "+(mode.equals("DOCUMENTS_AND_RECORDS")?"checked=\"true\"":"")+"/>" + Messages.getBodyString(locale,"MeridioConnector.DocumentsAndRecords") + "</nobr><br/>\n"+ " </td>\n"+ " </tr>\n"+ "</table>\n" ); } else { out.print( "<input type=\"hidden\" name=\""+seqPrefix+"specsearchon\" value=\""+mode+"\"/>\n" ); } // Security tab // Find whether security is on or off i = 0; boolean securityOn = true; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("security")) { String securityValue = sn.getAttributeValue("value"); if (securityValue.equals("off")) securityOn = false; else if (securityValue.equals("on")) securityOn = true; } } if (tabName.equals(Messages.getString(locale,"MeridioConnector.Security")) && connectionSequenceNumber == actualSequenceNumber) { out.print( "<table class=\"displaytable\">\n"+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.Security2") + "</nobr></td>\n"+ " <td class=\"value\">\n"+ " <input type=\"radio\" name=\""+seqPrefix+"specsecurity\" value=\"on\" "+(securityOn?"checked=\"true\"":"")+" />" + Messages.getBodyString(locale,"MeridioConnector.Enabled") + " \n"+ " <input type=\"radio\" name=\""+seqPrefix+"specsecurity\" value=\"off\" "+((securityOn==false)?"checked=\"true\"":"")+" />" + Messages.getBodyString(locale,"MeridioConnector.Disabled") + "\n"+ " </td>\n"+ " </tr>\n"+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n" ); // Go through forced ACL i = 0; k = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("access")) { String accessDescription = "_"+Integer.toString(k); String accessOpName = seqPrefix+"accessop"+accessDescription; String token = sn.getAttributeValue("token"); out.print( " <tr>\n"+ " <td class=\"description\">\n"+ " <input type=\"hidden\" name=\""+accessOpName+"\" value=\"\"/>\n"+ " <input type=\"hidden\" name=\""+seqPrefix+"spectoken"+accessDescription+"\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(token)+"\"/>\n"+ " <a name=\""+seqPrefix+"token_"+Integer.toString(k)+"\">\n"+ " <input type=\"button\" value=\"Delete\" onClick='Javascript:"+seqPrefix+"SpecOp(\""+accessOpName+"\",\"Delete\",\""+seqPrefix+"token_"+Integer.toString(k)+"\")' alt=\""+Messages.getAttributeString(locale,"MeridioConnector.DeleteToken")+Integer.toString(k)+"\"/>\n"+ " </a> \n"+ " </td>\n"+ " <td class=\"value\">\n"+ " "+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(token)+"\n"+ " </td>\n"+ " </tr>\n" ); k++; } } if (k == 0) { out.print( " <tr>\n"+ " <td class=\"message\" colspan=\"2\">" + Messages.getBodyString(locale,"MeridioConnector.NoAccessTokensPresent") + "</td>\n"+ " </tr>\n" ); } out.print( " <tr><td class=\"lightseparator\" colspan=\"2\"><hr/></td></tr>\n"+ " <tr>\n"+ " <td class=\"description\">\n"+ " <input type=\"hidden\" name=\""+seqPrefix+"tokencount\" value=\""+Integer.toString(k)+"\"/>\n"+ " <input type=\"hidden\" name=\""+seqPrefix+"accessop\" value=\"\"/>\n"+ " <a name=\""+seqPrefix+"token_"+Integer.toString(k)+"\">\n"+ " <input type=\"button\" value=\"Add\" onClick='Javascript:"+seqPrefix+"SpecAddAccessToken(\""+seqPrefix+"token_"+Integer.toString(k+1)+"\")' alt=\"" + Messages.getAttributeString(locale,"MeridioConnector.AddAccessToken") + "\"/>\n"+ " </a> \n"+ " </td>\n"+ " <td class=\"value\">\n"+ " <input type=\"text\" size=\"30\" name=\""+seqPrefix+"spectoken\" value=\"\"/>\n"+ " </td>\n"+ " </tr>\n"+ "</table>\n" ); } else { out.print( "<input type=\"hidden\" name=\""+seqPrefix+"specsecurity\" value=\""+(securityOn?"on":"off")+"\"/>\n" ); // Finally, go through forced ACL i = 0; k = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("access")) { String accessDescription = "_"+Integer.toString(k); String token = sn.getAttributeValue("token"); out.print( "<input type=\"hidden\" name=\""+seqPrefix+"spectoken"+accessDescription+"\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(token)+"\"/>\n" ); k++; } } out.print( "<input type=\"hidden\" name=\""+seqPrefix+"tokencount\" value=\""+Integer.toString(k)+"\"/>\n" ); } // Metadata tab // Find the path-value metadata attribute name i = 0; String pathNameAttribute = ""; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("pathnameattribute")) { pathNameAttribute = sn.getAttributeValue("value"); } } // Find the path-value mapping data i = 0; org.apache.manifoldcf.crawler.connectors.meridio.MatchMap matchMap = new org.apache.manifoldcf.crawler.connectors.meridio.MatchMap(); while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("pathmap")) { String pathMatch = sn.getAttributeValue("match"); String pathReplace = sn.getAttributeValue("replace"); matchMap.appendMatchPair(pathMatch,pathReplace); } } boolean allMetadata = false; HashMap metadataSelected = new HashMap(); i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("ReturnedMetadata")) { String category = sn.getAttributeValue("category"); String property = sn.getAttributeValue("property"); String descriptor; if (category == null || category.length() == 0) descriptor = property; else descriptor = category + "." + property; metadataSelected.put(descriptor,descriptor); } else if (sn.getType().equals("AllMetadata")) { String value = sn.getAttributeValue("value"); if (value != null && value.equals("true")) { allMetadata = true; } else allMetadata = false; } } if (tabName.equals(Messages.getString(locale,"MeridioConnector.Metadata")) && connectionSequenceNumber == actualSequenceNumber) { out.print( "<input type=\"hidden\" name=\""+seqPrefix+"specmappingcount\" value=\""+Integer.toString(matchMap.getMatchCount())+"\"/>\n"+ "<input type=\"hidden\" name=\""+seqPrefix+"specmappingop\" value=\"\"/>\n"+ "\n"+ "<table class=\"displaytable\">\n"+ " <tr><td class=\"separator\" colspan=\"4\"><hr/></td></tr>\n"+ " <tr>\n"+ " <td class=\"description\" colspan=\"1\">\n"+ " <nobr>" + Messages.getBodyString(locale,"MeridioConnector.IncludeAllMetadata") + "</nobr>\n"+ " </td>\n"+ " <td class=\"value\" colspan=\"3\">\n"+ " <nobr>\n"+ " <input type=\"radio\" name=\""+seqPrefix+"allmetadata\" value=\"false\" "+((allMetadata==false)?"checked=\"true\"":"")+">" + Messages.getBodyString(locale,"MeridioConnector.IncludeSpecified") + "</input>\n"+ " <input type=\"radio\" name=\""+seqPrefix+"allmetadata\" value=\"true\" "+(allMetadata?"checked=\"true\"":"")+">" + Messages.getBodyString(locale,"MeridioConnector.IncludeAll") + "</input>\n"+ " </nobr>\n"+ " </td>\n"+ " </tr>\n"+ " <tr><td class=\"separator\" colspan=\"4\"><hr/></td></tr>\n"+ " <tr>\n" ); // get the list of properties from the repository try { String[] propertyList; propertyList = getMeridioDocumentProperties(); out.print( " <td class=\"description\" colspan=\"1\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.Metadata") + "</nobr></td>\n"+ " <td class=\"value\" colspan=\"3\">\n"+ " <input type=\"hidden\" name=\""+seqPrefix+"specproperties_edit\" value=\"true\"/>\n" ); k = 0; while (k < propertyList.length) { String descriptor = propertyList[k++]; out.print( " <nobr>\n"+ " <input type=\"checkbox\" name=\""+seqPrefix+"specproperties\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(descriptor)+"\" "+((metadataSelected.get(descriptor)!=null)?"checked=\"true\"":"")+">\n"+ " "+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(descriptor)+"\n"+ " </input>\n"+ " </nobr>\n"+ " <br/>\n" ); } out.print( " </td>\n" ); } catch (ServiceInterruption e) { e.printStackTrace(); out.print( " <td class=\"message\" colspan=\"4\">" + Messages.getBodyString(locale,"MeridioConnector.ServiceInterruption") + org.apache.manifoldcf.ui.util.Encoder.bodyEscape(e.getMessage())+"</td>\n" ); } catch (ManifoldCFException e) { e.printStackTrace(); out.print( " <td class=\"message\" colspan=\"4\">"+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(e.getMessage())+"</td>\n" ); } out.print( " </tr>\n"+ " <tr><td class=\"separator\" colspan=\"4\"><hr/></td></tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.PathAttributeMetadataName") + "</nobr></td>\n"+ " <td class=\"value\" colspan=\"3\"><nobr>\n"+ " <input type=\"text\" size=\"16\" name=\""+seqPrefix+"specpathnameattribute\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(pathNameAttribute)+"\"/></nobr>\n"+ " </td>\n"+ " </tr>\n"+ " <tr><td class=\"separator\" colspan=\"4\"><hr/></td></tr>\n" ); i = 0; while (i < matchMap.getMatchCount()) { String matchString = matchMap.getMatchString(i); String replaceString = matchMap.getReplaceString(i); out.print( " <tr>\n"+ " <td class=\"description\"><input type=\"hidden\" name=\""+seqPrefix+"specmappingop_"+Integer.toString(i)+"\" value=\"\"/>\n"+ " <a name=\""+seqPrefix+"mapping_"+Integer.toString(i)+"\">\n"+ " <input type=\"button\" onClick='Javascript:"+seqPrefix+"SpecDeleteMapping(Integer.toString(i),\""+seqPrefix+"mapping_"+Integer.toString(i)+"\")' alt=\""+Messages.getAttributeString(locale,"MeridioConnector.DeleteMapping")+Integer.toString(i)+"\" value=\"Delete\"/>\n"+ " </a>\n"+ " </td>\n"+ " <td class=\"value\"><input type=\"hidden\" name=\""+seqPrefix+"specmatch_"+Integer.toString(i)+"\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(matchString)+"\"/>"+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(matchString)+"</td>\n"+ " <td class=\"value\">==></td>\n"+ " <td class=\"value\"><input type=\"hidden\" name=\""+seqPrefix+"specreplace_"+Integer.toString(i)+"\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(replaceString)+"\"/>"+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(replaceString)+"</td>\n"+ " </tr>\n" ); i++; } if (i == 0) { out.print( " <tr><td colspan=\"4\" class=\"message\">" + Messages.getBodyString(locale,"MeridioConnector.NoMappingsSpecified") + "</td></tr>\n" ); } out.print( " <tr><td class=\"lightseparator\" colspan=\"4\"><hr/></td></tr>\n"+ " <tr>\n"+ " <td class=\"description\">\n"+ " <a name=\""+seqPrefix+"mapping_"+Integer.toString(i)+"\">\n"+ " <input type=\"button\" onClick='Javascript:"+seqPrefix+"SpecAddMapping(\""+seqPrefix+"mapping_"+Integer.toString(i+1)+"\")' alt=\"" + Messages.getAttributeString(locale,"MeridioConnector.AddToMappings") + "\" value=\"Add\"/>\n"+ " </a>\n"+ " </td>\n"+ " <td class=\"value\">" + Messages.getBodyString(locale,"MeridioConnector.MatchRegexp") + " <input type=\"text\" name=\""+seqPrefix+"specmatch\" size=\"32\" value=\"\"/></td>\n"+ " <td class=\"value\">==></td>\n"+ " <td class=\"value\">" + Messages.getBodyString(locale,"MeridioConnector.ReplaceString") + " <input type=\"text\" name=\""+seqPrefix+"specreplace\" size=\"32\" value=\"\"/></td>\n"+ " </tr>\n"+ "</table>\n" ); } else { out.print( "<input type=\"hidden\" name=\""+seqPrefix+"specproperties_edit\" value=\"true\"/>\n" ); Iterator iter = metadataSelected.keySet().iterator(); while (iter.hasNext()) { String descriptor = (String)iter.next(); out.print( "<input type=\"hidden\" name=\""+seqPrefix+"specproperties\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(descriptor)+"\"/>\n" ); } out.print( "<input type=\"hidden\" name=\""+seqPrefix+"allmetadata\" value=\""+(allMetadata?"true":"false")+"\"/>\n"+ "<input type=\"hidden\" name=\""+seqPrefix+"specpathnameattribute\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(pathNameAttribute)+"\"/>\n"+ "<input type=\"hidden\" name=\""+seqPrefix+"specmappingcount\" value=\""+Integer.toString(matchMap.getMatchCount())+"\"/>\n" ); i = 0; while (i < matchMap.getMatchCount()) { String matchString = matchMap.getMatchString(i); String replaceString = matchMap.getReplaceString(i); out.print( "<input type=\"hidden\" name=\""+seqPrefix+"specmatch_"+Integer.toString(i)+"\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(matchString)+"\"/>\n"+ "<input type=\"hidden\" name=\""+seqPrefix+"specreplace_"+Integer.toString(i)+"\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(replaceString)+"\"/>\n" ); i++; } } } /** 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 always "editjob". * The connector will be connected before this method can be called. *@param variableContext contains the post data, including binary file-upload information. *@param locale is the locale the output is preferred to be in. *@param ds is the current document specification for this job. *@param connectionSequenceNumber is the unique number of this connection within the 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, Locale locale, Specification ds, int connectionSequenceNumber) throws ManifoldCFException { String seqPrefix = "s"+connectionSequenceNumber+"_"; int i; // Gather the path names String x = variableContext.getParameter(seqPrefix+"specpath_total"); if (x != null) { // Get rid of old specpath entries i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i); if (sn.getType().equals("SearchPath")) ds.removeChild(i); else i++; } // Gather into spec node, paying attention to any delete requests. i = 0; int count = Integer.parseInt(x); while (i < count) { String path = variableContext.getParameter(seqPrefix+"specpath_"+Integer.toString(i)); String pathOp = variableContext.getParameter(seqPrefix+"specpathop_"+Integer.toString(i)); if (pathOp == null || !pathOp.equals("Delete")) { SpecificationNode sn = new SpecificationNode("SearchPath"); sn.setAttribute("path",path); ds.addChild(ds.getChildCount(),sn); } i++; } // Do operation x = variableContext.getParameter(seqPrefix+"specpathop"); if (x != null) { // Retrieve current state information String pathSoFar = variableContext.getParameter(seqPrefix+"specpathbase"); String idsSoFar = variableContext.getParameter(seqPrefix+"specidsbase"); Integer containerType = new Integer(variableContext.getParameter(seqPrefix+"spectype")); if (x.equals("Add")) { // Tack the current path onto the specification SpecificationNode sn = new SpecificationNode("SearchPath"); sn.setAttribute("path",pathSoFar); ds.addChild(ds.getChildCount(),sn); pathSoFar = null; idsSoFar = null; containerType = null; } else if (x.equals("AddToPath")) { String pathField = variableContext.getParameter(seqPrefix+"specpath"); int index = pathField.indexOf(";"); int secondIndex = pathField.indexOf(";",index+1); pathSoFar = pathSoFar + pathField.substring(secondIndex+1) + "/"; idsSoFar = idsSoFar + "," + pathField.substring(0,index); containerType = new Integer(pathField.substring(index+1,secondIndex)); } else if (x.equals("DeleteFromPath")) { pathSoFar = pathSoFar.substring(0,pathSoFar.lastIndexOf("/")); pathSoFar = pathSoFar.substring(0,pathSoFar.lastIndexOf("/")+1); idsSoFar = idsSoFar.substring(0,idsSoFar.lastIndexOf(",")-1); containerType = new Integer(org.apache.manifoldcf.crawler.connectors.meridio.MeridioClassContents.CLASS); } currentContext.save(seqPrefix+"specpath",pathSoFar); currentContext.save(seqPrefix+"specpathids",idsSoFar); currentContext.save(seqPrefix+"specpathtype",containerType); } } // Searchon parameter x = variableContext.getParameter(seqPrefix+"specsearchon"); if (x != null) { i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i); if (sn.getType().equals("SearchOn")) ds.removeChild(i); else i++; } SpecificationNode newNode = new SpecificationNode("SearchOn"); newNode.setAttribute("value",x); ds.addChild(ds.getChildCount(),newNode); } // Categories parameter String[] y = variableContext.getParameterValues(seqPrefix+"speccategories"); if (y != null) { i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i); if (sn.getType().equals("SearchCategory")) ds.removeChild(i); else i++; } i = 0; while (i < y.length) { String category = y[i++]; SpecificationNode newNode = new SpecificationNode("SearchCategory"); newNode.setAttribute("category",category); ds.addChild(ds.getChildCount(),newNode); } } // Properties parameter x = variableContext.getParameter(seqPrefix+"specproperties_edit"); if (x != null && x.length() > 0) { i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i); if (sn.getType().equals("ReturnedMetadata")) ds.removeChild(i); else i++; } y = variableContext.getParameterValues(seqPrefix+"specproperties"); if (y != null) { i = 0; while (i < y.length) { String descriptor = y[i++]; SpecificationNode newNode = new SpecificationNode("ReturnedMetadata"); int index = descriptor.indexOf("."); String category; String property; if (index == -1) { category = null; property = descriptor; } else { category = descriptor.substring(0,index); property = descriptor.substring(index+1); } if (category != null) newNode.setAttribute("category",category); newNode.setAttribute("property",property); ds.addChild(ds.getChildCount(),newNode); } } } // Mime types parameter y = variableContext.getParameterValues(seqPrefix+"specmimetypes"); if (y != null) { i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i); if (sn.getType().equals("MIMEType")) ds.removeChild(i); else i++; } i = 0; while (i < y.length) { String category = y[i++]; SpecificationNode newNode = new SpecificationNode("MIMEType"); newNode.setAttribute("type",category); ds.addChild(ds.getChildCount(),newNode); } } x = variableContext.getParameter(seqPrefix+"specsecurity"); if (x != null) { // Delete all security entries first i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i); if (sn.getType().equals("security")) ds.removeChild(i); else i++; } SpecificationNode node = new SpecificationNode("security"); node.setAttribute("value",x); ds.addChild(ds.getChildCount(),node); } x = variableContext.getParameter(seqPrefix+"tokencount"); if (x != null) { // Delete all file specs first i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i); if (sn.getType().equals("access")) ds.removeChild(i); else i++; } int accessCount = Integer.parseInt(x); i = 0; while (i < accessCount) { String accessDescription = "_"+Integer.toString(i); String accessOpName = seqPrefix+"accessop"+accessDescription; String xc = variableContext.getParameter(accessOpName); if (xc != null && xc.equals("Delete")) { // Next row i++; continue; } // Get the stuff we need String accessSpec = variableContext.getParameter(seqPrefix+"spectoken"+accessDescription); SpecificationNode node = new SpecificationNode("access"); node.setAttribute("token",accessSpec); ds.addChild(ds.getChildCount(),node); i++; } String op = variableContext.getParameter(seqPrefix+"accessop"); if (op != null && op.equals("Add")) { String accessspec = variableContext.getParameter(seqPrefix+"spectoken"); SpecificationNode node = new SpecificationNode("access"); node.setAttribute("token",accessspec); ds.addChild(ds.getChildCount(),node); } } x = variableContext.getParameter(seqPrefix+"specpathnameattribute"); if (x != null && x.length() > 0) { i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i); if (sn.getType().equals("pathnameattribute")) ds.removeChild(i); else i++; } SpecificationNode node = new SpecificationNode("pathnameattribute"); node.setAttribute("value",x); ds.addChild(ds.getChildCount(),node); } x = variableContext.getParameter(seqPrefix+"specmappingcount"); if (x != null && x.length() > 0) { // Delete old spec i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i); if (sn.getType().equals("pathmap")) ds.removeChild(i); else i++; } // Now, go through the data and assemble a new list. int mappingCount = Integer.parseInt(x); // Gather up these i = 0; while (i < mappingCount) { String pathDescription = "_"+Integer.toString(i); String pathOpName = seqPrefix+"specmappingop"+pathDescription; x = variableContext.getParameter(pathOpName); if (x != null && x.equals("Delete")) { // Skip to the next i++; continue; } // Inserts won't happen until the very end String match = variableContext.getParameter(seqPrefix+"specmatch"+pathDescription); String replace = variableContext.getParameter(seqPrefix+"specreplace"+pathDescription); SpecificationNode node = new SpecificationNode("pathmap"); node.setAttribute("match",match); node.setAttribute("replace",replace); ds.addChild(ds.getChildCount(),node); i++; } // Check for add x = variableContext.getParameter(seqPrefix+"specmappingop"); if (x != null && x.equals("Add")) { String match = variableContext.getParameter(seqPrefix+"specmatch"); String replace = variableContext.getParameter(seqPrefix+"specreplace"); SpecificationNode node = new SpecificationNode("pathmap"); node.setAttribute("match",match); node.setAttribute("replace",replace); ds.addChild(ds.getChildCount(),node); } } x = variableContext.getParameter(seqPrefix+"allmetadata"); if (x != null) { i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i); if (sn.getType().equals("AllMetadata")) ds.removeChild(i); else i++; } SpecificationNode node = new SpecificationNode("AllMetadata"); node.setAttribute("value",x); ds.addChild(ds.getChildCount(),node); } 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. * The connector will be connected before this method can be called. *@param out is the output to which any HTML should be sent. *@param locale is the locale the output is preferred to be in. *@param ds is the current document specification for this job. *@param connectionSequenceNumber is the unique number of this connection within the job. */ @Override public void viewSpecification(IHTTPOutput out, Locale locale, Specification ds, int connectionSequenceNumber) throws ManifoldCFException, IOException { out.print( "<table class=\"displaytable\">\n"+ " <tr>\n" ); int i = 0; boolean seenAny = false; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("SearchPath")) { if (seenAny == false) { out.print( " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.Paths") + "</nobr></td>\n"+ " <td class=\"value\">\n" ); seenAny = true; } out.print( " "+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(sn.getAttributeValue("path"))+"<br/>\n" ); } } if (seenAny) { out.print( " </td>\n"+ " </tr>\n" ); } else { out.print( " <tr><td class=\"message\" colspan=\"2\">" + Messages.getBodyString(locale,"MeridioConnector.NoPathsSpecified") + "</td></tr>\n" ); } out.print( " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.DataType") + "</nobr>\n"+ " </td>\n"+ " <td class=\"value\">\n"+ " <nobr>\n" ); i = 0; String mode = "DOCUMENTS_AND_RECORDS"; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("SearchOn")) mode = sn.getAttributeValue("value"); } String displayMode; if (mode.equals("DOCUMENTS")) displayMode = "Documents only"; else if (mode.equals("RECORDS")) displayMode = "Records only"; else displayMode = "Documents and Records"; out.print( " "+displayMode+"\n"+ " </nobr>\n"+ " </td>\n"+ " </tr>\n"+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.Categories") + "</nobr>\n"+ " </td>\n"+ " <td class=\"value\">\n" ); int count = 0; i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("SearchCategory")) count++; } String[] sortArray = new String[count]; count = 0; i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("SearchCategory")) sortArray[count++] = sn.getAttributeValue("category"); } java.util.Arrays.sort(sortArray); i = 0; while (i < sortArray.length) { String category = sortArray[i++]; out.print( " <nobr>"+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(category)+"</nobr><br/>\n" ); } out.print( " </td>\n"+ " </tr>\n"+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+ " <tr>\n"+ " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.MimeTypes") + "</nobr>\n"+ " </td>\n"+ " <td class=\"value\">\n" ); count = 0; i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("MIMEType")) count++; } sortArray = new String[count]; count = 0; i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("MIMEType")) sortArray[count++] = sn.getAttributeValue("type"); } java.util.Arrays.sort(sortArray); i = 0; while (i < sortArray.length) { String mimeType = sortArray[i++]; out.print( " <nobr>"+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(mimeType)+"</nobr><br/>\n" ); } out.print( " </td>\n"+ " </tr>\n"+ "\n"+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+ "\n" ); // Find whether security is on or off i = 0; boolean securityOn = true; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("security")) { String securityValue = sn.getAttributeValue("value"); if (securityValue.equals("off")) securityOn = false; else if (securityValue.equals("on")) securityOn = true; } } out.print( " <tr>\n"+ " <td class=\"description\">" + Messages.getBodyString(locale,"MeridioConnector.Security2") + "</td>\n"+ " <td class=\"value\">"+(securityOn?Messages.getBodyString(locale,"MeridioConnector.Enabled"):Messages.getBodyString(locale,"MeridioConnector.Disabled"))+"</td>\n"+ " </tr>\n"+ "\n"+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n" ); // Go through looking for access tokens seenAny = false; i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("access")) { if (seenAny == false) { out.print( " <tr>\n"+ " <td class=\"description\">" + Messages.getBodyString(locale,"MeridioConnector.AccessTokens") + "</td>\n"+ " <td class=\"value\">\n" ); seenAny = true; } String token = sn.getAttributeValue("token"); out.print( " "+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(token)+"<br/>\n" ); } } if (seenAny) { out.print( " </td>\n"+ " </tr>\n" ); } else { out.print( " <tr><td class=\"message\" colspan=\"2\">" + Messages.getBodyString(locale,"MeridioConnector.NoAccessTokensSpecified") + "</td></tr>\n" ); } out.print( " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+ " <tr>\n" ); count = 0; i = 0; boolean allMetadata = false; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("ReturnedMetadata")) count++; else if (sn.getType().equals("AllMetadata")) { String value = sn.getAttributeValue("value"); if (value != null && value.equals("true")) { allMetadata = true; } } } if (allMetadata) { out.print( " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.MetadataPropertiesToIngest") + "</nobr>\n"+ " </td>\n"+ " <td class=\"value\"><nobr><b>" + Messages.getBodyString(locale,"MeridioConnector.AllMetadata") + "</b></nobr></td>\n" ); } else if (count > 0) { out.print( " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.MetadataPropertiesToIngest") + "</nobr>\n"+ " </td>\n"+ " <td class=\"value\">\n" ); sortArray = new String[count]; i = 0; count = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("ReturnedMetadata")) { String category = sn.getAttributeValue("category"); String property = sn.getAttributeValue("property"); String descriptor; if (category == null || category.length() == 0) descriptor = property; else descriptor = category + "." + property; sortArray[count++] = descriptor; } } java.util.Arrays.sort(sortArray); i = 0; while (i < sortArray.length) { String descriptor = sortArray[i++]; out.print( " <nobr>"+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(descriptor)+"</nobr><br/>\n" ); } out.print( " </td>\n" ); } else { out.print( " <td class=\"message\" colspan=\"2\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.NoMetadataPropertiesToIngest") + "</nobr></td> \n" ); } out.print( " </tr>\n"+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n" ); // Find the path-name metadata attribute name i = 0; String pathNameAttribute = ""; i = 0; while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("pathnameattribute")) { pathNameAttribute = sn.getAttributeValue("value"); } } out.print( " <tr>\n" ); if (pathNameAttribute.length() > 0) { out.print( " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.PathNameMetadataAttribute") + "</nobr></td>\n"+ " <td class=\"value\"><nobr>"+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(pathNameAttribute)+"</nobr></td>\n" ); } else { out.print( " <td class=\"message\" colspan=\"2\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.NoPathNameMetadataAttributeSpecified") + "</nobr></td>\n" ); } out.print( " </tr>\n"+ "\n"+ " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+ "\n"+ " <tr>\n" ); // Find the path-value mapping data i = 0; org.apache.manifoldcf.crawler.connectors.meridio.MatchMap matchMap = new org.apache.manifoldcf.crawler.connectors.meridio.MatchMap(); while (i < ds.getChildCount()) { SpecificationNode sn = ds.getChild(i++); if (sn.getType().equals("pathmap")) { String pathMatch = sn.getAttributeValue("match"); String pathReplace = sn.getAttributeValue("replace"); matchMap.appendMatchPair(pathMatch,pathReplace); } } if (matchMap.getMatchCount() > 0) { out.print( " <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"MeridioConnector.PathValueMapping") + "</nobr></td>\n"+ " <td class=\"value\">\n"+ " <table class=\"displaytable\">\n" ); i = 0; while (i < matchMap.getMatchCount()) { String matchString = matchMap.getMatchString(i); String replaceString = matchMap.getReplaceString(i); out.print( " <tr>\n"+ " <td class=\"value\">"+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(matchString)+"</td>\n"+ " <td class=\"value\">--></td><td class=\"value\">"+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(replaceString)+"</td>\n"+ " </tr>\n" ); i++; } out.print( " </table>\n"+ " </td>\n" ); } else { out.print( " <td class=\"message\" colspan=\"2\"><nobr>"+Messages.getBodyString(locale,"MeridioConnector.NoMappingsSpecified")+"</nobr></td>\n" ); } out.print( " </tr>\n"+ "</table>\n" ); } // Protected methods /** Grab forced acl out of document specification. *@param spec is the document specification. *@return the acls. */ protected static String[] getAcls(Specification spec) { HashMap map = new HashMap(); int i = 0; boolean securityOn = true; while (i < spec.getChildCount()) { SpecificationNode sn = spec.getChild(i++); if (sn.getType().equals("access")) { String token = sn.getAttributeValue("token"); map.put(token,token); } else if (sn.getType().equals("security")) { String value = sn.getAttributeValue("value"); if (value.equals("on")) securityOn = true; else if (value.equals("off")) securityOn = false; } } if (!securityOn) return null; String[] rval = new String[map.size()]; Iterator iter = map.keySet().iterator(); i = 0; while (iter.hasNext()) { rval[i++] = (String)iter.next(); } return rval; } private static String [] getMIMETypes ( Specification spec ) { ArrayList al = new ArrayList (); for (int i = 0; i < spec.getChildCount(); i++) { SpecificationNode sn = spec.getChild(i); if (sn.getType().equals("MIMEType")) { al.add(sn.getAttributeValue("type")); } } String [] mimeTypes = new String[al.size()]; Iterator it = al.iterator(); for (int i = 0; it.hasNext(); i++) { mimeTypes[i] = (String) it.next(); } return mimeTypes; } /** Returns all objects from the Meridio repository matching the document specification, * and constrained by the start/end object addition times, and the subset of the total * results to return (startPositionOfHits and maxHitsToReturn) * * @see documentSpecificationSearch Specification docSpec, long startTime, long endTime, int startPositionOfHits, int maxHitsToReturn, int restrictDocumentId */ private DMSearchResults documentSpecificationSearch ( Specification docSpec, // The castor representation of the Document Specification long startTime, long endTime, int startPositionOfHits, int maxHitsToReturn ) throws RemoteException, MeridioDataSetException { return documentSpecificationSearch(docSpec, startTime, endTime, startPositionOfHits, maxHitsToReturn, null, null); } /** Returns objects from the Meridio repository matching the document specification, * and constrained by the start/end object addition times, and the subset of the total * results to return (startPositionOfHits and maxHitsToReturn) * * @param docSpec the criteria to determine if an object should be returned * @param startTime the date/time after which the object must have been added (inclusive) * @param endTime the date/time before which the object must have been added (exclusive) * @param startPositionOfHits the starting position in the hits to begin returning results from * @param maxHitsToReturn the maximum number of hits to return * @param restrictDocumentId if zero, then consider all objects, otherwise if set consider only * the indicated document identifier - this is used to check if a * give document id subsequently matches the document specification * at some point after it was initially returned from the search results * * @see documentSpecificationSearch Specification docSpec, long startTime, long endTime, int startPositionOfHits, int maxHitsToReturn, int [] restrictDocumentId */ private DMSearchResults documentSpecificationSearch ( Specification docSpec, // The castor representation of the Document Specification long startTime, long endTime, int startPositionOfHits, int maxHitsToReturn, long restrictDocumentId ) throws RemoteException, MeridioDataSetException { if (restrictDocumentId > 0) { return documentSpecificationSearch(docSpec, startTime, endTime, startPositionOfHits, maxHitsToReturn, new long [] {restrictDocumentId}, null); } else { return documentSpecificationSearch(docSpec, startTime, endTime, startPositionOfHits, maxHitsToReturn, null, null); } } /** Returns objects from the Meridio repository matching the document specification, * and constrained by the start/end object addition times, and the subset of the total * results to return (startPositionOfHits and maxHitsToReturn) * * The search method can return the results in "batches" results, based on the start position * and maximum hits to return. * * @param docSpec the criteria to determine if an object should be returned * @param startTime the date/time after which the object must have been added (inclusive) * @param endTime the date/time before which the object must have been added (exclusive) * @param startPositionOfHits the starting position in the hits to begin returning results from * @param maxHitsToReturn the maximum number of hits to return * @param restrictDocumentId if the array is empty then return all matching objects, otherwise * * Search results are returned in the SEARCHRESULTS_DOCUMENTS DataTable. * *@throws RemoteException if an error is encountered call the Meridio web service method(s) *@throws MeridioDataSetException if an error is encountered manipulating the Meridio DataSet */ protected DMSearchResults documentSpecificationSearch ( Specification docSpec, long startTime, long endTime, int startPositionOfHits, int maxHitsToReturn, long [] restrictDocumentId, ReturnMetadata[] returnMetadata ) throws RemoteException, MeridioDataSetException { try { Logging.connectors.debug("Entering documentSpecificationSearch"); int currentSearchTerm = 1; DMDataSet dsSearchCriteria = new DMDataSet(); /*==================================================================== * Exclude things marked for delete *===================================================================*/ PROPERTY_TERMS drDeleteSearch = new PROPERTY_TERMS(); drDeleteSearch.setId(currentSearchTerm++); drDeleteSearch.setTermType(new Short("1").shortValue()); //0=STRING, 1=NUMBER, 2=DATE drDeleteSearch.setPropertyName("PROP_markedForDelete"); drDeleteSearch.setCategoryId(4); //Global Standard/Fixed Property drDeleteSearch.setNum_relation(new Short("0").shortValue()); //dmNumRelation.EQUAL drDeleteSearch.setNum_value(0); drDeleteSearch.setParentId(1); drDeleteSearch.setIsVersionProperty(false); dsSearchCriteria.addPROPERTY_TERMS(drDeleteSearch); /*==================================================================== * Restrict based on start & end date/time, if necessssary *===================================================================*/ if (startTime > 0L) { Logging.connectors.debug("Start Date/time is <" + new Date(startTime) + "> in ms <" + startTime + ">" + " End Date/time is <" + new Date(endTime) + "> in ms <" + endTime + ">"); PROPERTY_TERMS drDateStart = new PROPERTY_TERMS(); drDateStart.setId(currentSearchTerm++); drDateStart.setTermType(new Short("2").shortValue()); //0=STRING, 1=NUMBER, 2=DATE drDateStart.setPropertyName("PROP_lastModifiedDate"); drDateStart.setCategoryId(4); //Global Standard/Fixed Property drDateStart.setDate_relation(new Short("11").shortValue()); //dtONORAFTER drDateStart.setDate_value(new Date(startTime)); drDateStart.setParentId(1); drDateStart.setIsVersionProperty(false); dsSearchCriteria.addPROPERTY_TERMS(drDateStart); PROPERTY_TERMS drDateEnd = new PROPERTY_TERMS(); drDateEnd.setId(currentSearchTerm++); drDateEnd.setTermType(new Short("2").shortValue()); //0=STRING, 1=NUMBER, 2=DATE drDateEnd.setPropertyName("PROP_lastModifiedDate"); drDateEnd.setCategoryId(4); //Global Standard/Fixed Property drDateEnd.setDate_relation(new Short("8").shortValue()); //dtBEFORE drDateEnd.setDate_value(new Date(endTime)); drDateEnd.setParentId(1); drDateEnd.setIsVersionProperty(false); dsSearchCriteria.addPROPERTY_TERMS(drDateEnd); } /*==================================================================== * Just add a dummy term to make the conditional logic easier; i.e. * always add an "AND" - the dummy term is required in case there are * no other search criteria - i.e. we could be searching the whole * Meridio repository * * Search for document id's which are > 0 - this will always be true *===================================================================*/ PROPERTY_TERMS drDocIdSearch = new PROPERTY_TERMS(); drDocIdSearch.setId(currentSearchTerm++); drDocIdSearch.setTermType(new Short("1").shortValue()); //0=STRING, 1=NUMBER, 2=DATE drDocIdSearch.setPropertyName("PROP_documentId"); drDocIdSearch.setCategoryId(4); //Global Standard/Fixed Property drDocIdSearch.setNum_relation(new Short("3").shortValue()); //dmNumRelation.GREATER drDocIdSearch.setNum_value(0); drDocIdSearch.setParentId(1); drDocIdSearch.setIsVersionProperty(false); dsSearchCriteria.addPROPERTY_TERMS(drDocIdSearch); if (restrictDocumentId != null && restrictDocumentId.length == 1) { /*==================================================================== * Restrict the search query to just the 1 document ID passed in *===================================================================*/ PROPERTY_TERMS drDocIdSearchRestricted = new PROPERTY_TERMS(); drDocIdSearchRestricted.setId(currentSearchTerm++); drDocIdSearchRestricted.setTermType(new Short("1").shortValue()); //0=STRING, 1=NUMBER, 2=DATE drDocIdSearchRestricted.setPropertyName("PROP_documentId"); drDocIdSearchRestricted.setCategoryId(4); //Global Standard/Fixed Property drDocIdSearchRestricted.setNum_relation(new Short("0").shortValue()); //dmNumRelation.EQUAL drDocIdSearchRestricted.setNum_value(restrictDocumentId[0]); //Search for the specific doc ID drDocIdSearchRestricted.setParentId(1); drDocIdSearchRestricted.setIsVersionProperty(false); dsSearchCriteria.addPROPERTY_TERMS(drDocIdSearchRestricted); } else if (restrictDocumentId != null && restrictDocumentId.length > 1) { /*==================================================================== * Multiple document id's have been passed in, so we need to "or" * them together *===================================================================*/ for (int i = 0; i < restrictDocumentId.length; i++) { PROPERTY_TERMS drDocIdSearchRestricted = new PROPERTY_TERMS(); drDocIdSearchRestricted.setId(currentSearchTerm++); drDocIdSearchRestricted.setTermType(new Short("1").shortValue()); //0=STRING, 1=NUMBER, 2=DATE drDocIdSearchRestricted.setPropertyName("PROP_documentId"); drDocIdSearchRestricted.setCategoryId(4); //Global Standard/Fixed Property drDocIdSearchRestricted.setNum_relation(new Short("0").shortValue()); //dmNumRelation.EQUAL drDocIdSearchRestricted.setNum_value(restrictDocumentId[i]); //Search for the specific doc ID drDocIdSearchRestricted.setParentId(4); drDocIdSearchRestricted.setIsVersionProperty(false); dsSearchCriteria.addPROPERTY_TERMS(drDocIdSearchRestricted); } PROPERTY_OPS drMIMETypeOps = new PROPERTY_OPS(); drMIMETypeOps.setId(4); drMIMETypeOps.setParentId(1); drMIMETypeOps.setOperator(new Short("1").shortValue()); // OR dsSearchCriteria.addPROPERTY_OPS(drMIMETypeOps); } PROPERTY_OPS drPropertyOps = new PROPERTY_OPS(); drPropertyOps.setId(1); drPropertyOps.setOperator(new Short("0").shortValue()); //AND dsSearchCriteria.addPROPERTY_OPS(drPropertyOps); /*==================================================================== * Filter on documents, records, or documents and records * * The "SearchDocuments" method returns both documents and records; to * return just documents, get things where the recordType is not * 0, 4 or 19 (refer to Meridio Documentation) *===================================================================*/ String searchOn = null; for (int i = 0; i < docSpec.getChildCount(); i++) { SpecificationNode sn = docSpec.getChild(i); if (sn.getType().equals("SearchOn")) { searchOn = sn.getAttributeValue("value"); } } if (searchOn != null && searchOn.equals("DOCUMENTS_ONLY")) { PROPERTY_TERMS drDocsOrRecsSearch = new PROPERTY_TERMS(); drDocsOrRecsSearch.setId(currentSearchTerm++); drDocsOrRecsSearch.setTermType(new Short("1").shortValue()); //0=STRING, 1=NUMBER, 2=DATE drDocsOrRecsSearch.setPropertyName("PROP_recordType"); drDocsOrRecsSearch.setCategoryId(4); //Global Standard/Fixed Property drDocsOrRecsSearch.setNum_relation(new Short("1").shortValue()); //dmNumberRelation.NOTEQUAL=1 drDocsOrRecsSearch.setNum_value(0); drDocsOrRecsSearch.setParentId(1); drDocsOrRecsSearch.setIsVersionProperty(false); dsSearchCriteria.addPROPERTY_TERMS(drDocsOrRecsSearch); PROPERTY_TERMS drDocsOrRecsSearch2 = new PROPERTY_TERMS(); drDocsOrRecsSearch2.setId(currentSearchTerm++); drDocsOrRecsSearch2.setTermType(new Short("1").shortValue()); //0=STRING, 1=NUMBER, 2=DATE drDocsOrRecsSearch2.setPropertyName("PROP_recordType"); drDocsOrRecsSearch2.setCategoryId(4); //Global Standard/Fixed Property drDocsOrRecsSearch2.setNum_relation(new Short("1").shortValue()); //dmNumberRelation.NOTEQUAL=1 drDocsOrRecsSearch2.setNum_value(4); drDocsOrRecsSearch2.setParentId(1); drDocsOrRecsSearch2.setIsVersionProperty(false); dsSearchCriteria.addPROPERTY_TERMS(drDocsOrRecsSearch2); PROPERTY_TERMS drDocsOrRecsSearch3 = new PROPERTY_TERMS(); drDocsOrRecsSearch3.setId(currentSearchTerm++); drDocsOrRecsSearch3.setTermType(new Short("1").shortValue()); //0=STRING, 1=NUMBER, 2=DATE drDocsOrRecsSearch3.setPropertyName("PROP_recordType"); drDocsOrRecsSearch3.setCategoryId(4); //Global Standard/Fixed Property drDocsOrRecsSearch3.setNum_relation(new Short("1").shortValue()); //dmNumberRelation.NOTEQUAL=1 drDocsOrRecsSearch3.setNum_value(19); drDocsOrRecsSearch3.setParentId(1); drDocsOrRecsSearch3.setIsVersionProperty(false); dsSearchCriteria.addPROPERTY_TERMS(drDocsOrRecsSearch3); } /*==================================================================== * Filter on documents, records, or documents and records * * The "SearchDocuments" method returns both documents and records; to * return just records, get things where the recordType is 4 or greater *===================================================================*/ if (searchOn != null && searchOn.equals("RECORDS_ONLY")) { PROPERTY_TERMS drDocsOrRecsSearch = new PROPERTY_TERMS(); drDocsOrRecsSearch.setId(currentSearchTerm++); drDocsOrRecsSearch.setTermType(new Short("1").shortValue()); //0=STRING, 1=NUMBER, 2=DATE drDocsOrRecsSearch.setPropertyName("PROP_recordType"); drDocsOrRecsSearch.setCategoryId(4); //Global Standard/Fixed Property drDocsOrRecsSearch.setNum_relation(new Short("5").shortValue()); //dmNumberRelation.GREATEROREQUAL=5 drDocsOrRecsSearch.setNum_value(4); drDocsOrRecsSearch.setParentId(1); drDocsOrRecsSearch.setIsVersionProperty(false); dsSearchCriteria.addPROPERTY_TERMS(drDocsOrRecsSearch); } /*==================================================================== * Filter on class or folder (if any) *===================================================================*/ for (int i = 0; i < docSpec.getChildCount(); i++) { SpecificationNode sn = docSpec.getChild(i); if (sn.getType().equals("SearchPath")) { String searchPath = sn.getAttributeValue("path"); int searchContainer = meridio_.findClassOrFolder(searchPath); if (searchContainer > 0) { SEARCH_CONTAINERS drSearchContainers = new SEARCH_CONTAINERS(); drSearchContainers.setContainerId(searchContainer); dsSearchCriteria.addSEARCH_CONTAINERS(drSearchContainers); Logging.connectors.debug("Found path [" + searchPath + "] id: [" + searchContainer + "]"); } else if (searchContainer == 0) { Logging.connectors.debug("Meridio: Found FilePlan root, so not including in search criteria!"); } else { /*==================================================================== * We can't find the path, so ignore it. * * This is potentially opening up the search scope, i.e. if there was * one path which was being searched and then the Meridio FilePlan is * re-organised and the path no longer exists (but the original content * has just been moved in the tree) then this could cause all the * Meridio content to be returned *===================================================================*/ Logging.connectors.warn("Meridio: Did not find FilePlan path [" + searchPath + "]. " + "The path is therefore *not* being used to restrict the search scope"); } } } /*==================================================================== * Filter on category (if any) *===================================================================*/ CATEGORIES [] meridioCategories = meridio_.getCategories().getCATEGORIES(); // Create a map from title to category ID HashMap categoryMap = new HashMap(); int i = 0; while (i < meridioCategories.length) { String title = meridioCategories[i].getPROP_title(); long categoryID = meridioCategories[i].getPROP_categoryId(); categoryMap.put(title,new Long(categoryID)); i++; } ArrayList categoriesToAdd = new ArrayList (); for (i = 0; i < docSpec.getChildCount(); i++) { SpecificationNode sn = docSpec.getChild(i); if (sn.getType().equals("SearchCategory")) { String searchCategory = sn.getAttributeValue("category"); Long categoryIDObject = (Long)categoryMap.get(searchCategory); if (categoryIDObject != null) { if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Category [" + searchCategory + "] match, ID=[" + categoryIDObject + "]"); categoriesToAdd.add(categoryIDObject); } else { if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: No match found for Category [" + searchCategory + "]"); } } } for (i = 0; i < categoriesToAdd.size(); i++) { PROPERTY_TERMS drDocsOrRecsSearch = new PROPERTY_TERMS(); drDocsOrRecsSearch.setId(currentSearchTerm++); drDocsOrRecsSearch.setTermType(new Short("1").shortValue()); //0=STRING, 1=NUMBER, 2=DATE drDocsOrRecsSearch.setPropertyName("PROP_categoryId"); drDocsOrRecsSearch.setCategoryId(4); //Global Standard/Fixed Property drDocsOrRecsSearch.setNum_relation(new Short("0").shortValue()); //dmNumberRelation.GREATEROREQUAL=5 drDocsOrRecsSearch.setNum_value(((Long) categoriesToAdd.get(i)).longValue()); if (categoriesToAdd.size() == 1) // If there is one term, we can use the AND clause { drDocsOrRecsSearch.setParentId(1); } else // Otherwise, need to have an OR subclause { drDocsOrRecsSearch.setParentId(2); } drDocsOrRecsSearch.setIsVersionProperty(false); dsSearchCriteria.addPROPERTY_TERMS(drDocsOrRecsSearch); } /*==================================================================== * Filter on MIME Type (if any are in the Document Specification) *===================================================================*/ String [] mimeTypes = getMIMETypes(docSpec); for (i = 0; i < mimeTypes.length; i++) { PROPERTY_TERMS drMIMETypesSearch = new PROPERTY_TERMS(); drMIMETypesSearch.setId(currentSearchTerm++); drMIMETypesSearch.setTermType(new Short("0").shortValue()); //0=STRING, 1=NUMBER, 2=DATE drMIMETypesSearch.setPropertyName("PROP_W_mimeType"); drMIMETypesSearch.setCategoryId(4); //Global Standard/Fixed Property drMIMETypesSearch.setStr_relation(new Short("0").shortValue()); //dmNumberRelation.GREATEROREQUAL=5 drMIMETypesSearch.setStr_value(mimeTypes[i]); if (mimeTypes.length == 1) // If there is one term, we can use the AND clause { drMIMETypesSearch.setParentId(1); } else // Otherwise, need to have an OR subclause { drMIMETypesSearch.setParentId(3); } drMIMETypesSearch.setIsVersionProperty(true); dsSearchCriteria.addPROPERTY_TERMS(drMIMETypesSearch); } if (categoriesToAdd.size() > 1) { PROPERTY_OPS drCategoryOps = new PROPERTY_OPS(); drCategoryOps.setId(2); drCategoryOps.setParentId(1); drCategoryOps.setOperator(new Short("1").shortValue()); // OR dsSearchCriteria.addPROPERTY_OPS(drCategoryOps); } if (mimeTypes.length > 1) { PROPERTY_OPS drMIMETypeOps = new PROPERTY_OPS(); drMIMETypeOps.setId(3); drMIMETypeOps.setParentId(1); drMIMETypeOps.setOperator(new Short("1").shortValue()); // OR dsSearchCriteria.addPROPERTY_OPS(drMIMETypeOps); } /*==================================================================== * Define what is being returned: include the properties that are * present within the document specification *===================================================================*/ int returnResultsAdded = 0; if (returnMetadata != null && returnMetadata.length > 0) { PROPERTYDEFS [] propertyDefs = meridio_.getStaticData().getPROPERTYDEFS(); // Build a hash table containing standard and custom properties HashMap propertyMap = new HashMap(); HashMap customMap = new HashMap(); i = 0; while (i < propertyDefs.length) { PROPERTYDEFS def = propertyDefs[i++]; if (def.getTableName().equals("DOCUMENTS")) { propertyMap.put(def.getDisplayName(),def.getColumnName()); } else if (def.getTableName().equals("DOCUMENT_CUSTOMPROPS")) { Long categoryID = new Long(def.getCategoryId()); HashMap dataMap = (HashMap)customMap.get(categoryID); if (dataMap == null) { dataMap = new HashMap(); customMap.put(categoryID,dataMap); } dataMap.put(def.getDisplayName(),def.getColumnName()); } } for (i = 0; i < returnMetadata.length; i++) { long categoryMatch = 0; boolean isCategoryMatch = false; RESULTDEFS drResultDefs = new RESULTDEFS(); drResultDefs.setIsVersionProperty(false); if (returnMetadata[i].getCategoryName() == null || returnMetadata[i].getCategoryName().length() == 0) { isCategoryMatch = true; categoryMatch = 4; } else { Long categoryIDObject = (Long)categoryMap.get(returnMetadata[i].getCategoryName()); if (categoryIDObject != null) { isCategoryMatch = true; categoryMatch = categoryIDObject.longValue(); } } if (!isCategoryMatch) { if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: Category '" + returnMetadata[i].getCategoryName() + "' no match found for search results criteria!"); continue; } else { /*==================================================================== * Find the matching property name for the display name (as it is the * property column name that is required by the search) *===================================================================*/ String columnName = (String)propertyMap.get(returnMetadata[i].getPropertyName()); if (columnName == null) { HashMap categoryMatchMap = (HashMap)customMap.get(new Long(categoryMatch)); if (categoryMatchMap != null) { columnName = (String)categoryMatchMap.get(returnMetadata[i].getPropertyName()); } } if (columnName != null) drResultDefs.setPropertyName(columnName); else { if (Logging.connectors.isDebugEnabled()) Logging.connectors.debug("Meridio: No property match found for '" + returnMetadata[i].getPropertyName() + "'"); continue; } drResultDefs.setCategoryId(categoryMatch); dsSearchCriteria.addRESULTDEFS(drResultDefs); returnResultsAdded++; } } } /*==================================================================== * We always need to return something in the search results, so add * the last modified date if nothing else was provided *===================================================================*/ if (returnResultsAdded == 0) { RESULTDEFS drResultDefs = new RESULTDEFS(); drResultDefs.setPropertyName("PROP_lastModifiedDate"); drResultDefs.setIsVersionProperty(false); drResultDefs.setCategoryId(4); dsSearchCriteria.addRESULTDEFS(drResultDefs); } /*==================================================================== * Call the search method *===================================================================*/ DMSearchResults searchResults = meridio_.searchDocuments(dsSearchCriteria, maxHitsToReturn, startPositionOfHits, DmPermission.READ, false, DmSearchScope.BOTH, false, true, false, DmLogicalOp.AND); return searchResults; } finally { Logging.connectors.debug("Exiting documentSpecificationSearch method."); } } private static class ReturnMetadata { protected String categoryName_; protected String propertyName_; public ReturnMetadata ( String categoryName, String propertyName ) { categoryName_ = categoryName; propertyName_ = propertyName; } public String getCategoryName () { return categoryName_; } public String getPropertyName () { return propertyName_; } } /** Returns the categories set up in the Meridio system; these are used by the UI for two * purposes * * 1) To populate the "SearchCategory" * Use "getPROP_title()" on the list of CATEGORIES object in * the return ArrayList * 2) To assist with population of the metadata values to return. The * available metadata depends on the chosen category * *@return Sorted array of strings containing the category names */ public String [] getMeridioCategories () throws ManifoldCFException, ServiceInterruption { Logging.connectors.debug("Entering 'getMeridioCategories' method"); while (true) { getSession(); ArrayList returnCategories = new ArrayList(); try { CATEGORIES [] categories = meridio_.getCategories().getCATEGORIES(); for (int i = 0; i < categories.length; i++) { if (categories[i].getPROP_categoryId() == 4 || // Global Document Category categories[i].getPROP_categoryId() == 5 || // Mail Message categories[i].getPROP_categoryId() > 100) // Custom Document Category { if (!categories[i].getPROP_title().equals("<None>")) { Logging.connectors.debug("Adding category <" + categories[i].getPROP_title() + ">"); returnCategories.add(categories[i].getPROP_title()); } } } String [] returnStringArray = new String[returnCategories.size()]; Iterator it = returnCategories.iterator(); for (int i = 0; it.hasNext(); i++) { returnStringArray[i] = (String) it.next(); } java.util.Arrays.sort(returnStringArray); Logging.connectors.debug("Exiting 'getMeridioCategories' method"); return returnStringArray; } catch (org.apache.axis.AxisFault e) { long currentTime = System.currentTimeMillis(); if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://xml.apache.org/axis/","HTTP"))) { org.w3c.dom.Element elem = e.lookupFaultDetail(new javax.xml.namespace.QName("http://xml.apache.org/axis/","HttpErrorCode")); if (elem != null) { elem.normalize(); String httpErrorCode = elem.getFirstChild().getNodeValue().trim(); throw new ManifoldCFException("Unexpected http error code "+httpErrorCode+" getting categories: "+e.getMessage()); } throw new ManifoldCFException("Unknown http error occurred while getting categories: "+e.getMessage(),e); } if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://schemas.xmlsoap.org/soap/envelope/","Server.userException"))) { String exceptionName = e.getFaultString(); if (exceptionName.equals("java.lang.InterruptedException")) throw new ManifoldCFException("Interrupted",ManifoldCFException.INTERRUPTED); } if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://schemas.xmlsoap.org/soap/envelope/","Server"))) { if (e.getFaultString().indexOf(" 23031#") != -1) { // This means that the session has expired, so reset it and retry meridio_ = null; continue; } } throw new ManifoldCFException("Meridio: Got an unknown remote exception getting categories - axis fault = "+e.getFaultCode().getLocalPart()+", detail = "+e.getFaultString(),e); } catch (RemoteException remoteException) { throw new ManifoldCFException("Meridio: A Remote Exception occurred while " + "retrieving the Meridio categories: "+remoteException.getMessage(), remoteException); } catch (MeridioDataSetException meridioDataSetException) { throw new ManifoldCFException("Meridio: DataSet Exception occurred retrieving the Meridio categories: "+meridioDataSetException.getMessage(), meridioDataSetException); } } } public String [] getMeridioDocumentProperties () throws ManifoldCFException, ServiceInterruption { Logging.connectors.debug("Entering 'getMeridioDocumentProperties' method"); while (true) { getSession(); ArrayList meridioDocumentProperties = new ArrayList(); try { CATEGORIES [] categories = meridio_.getCategories().getCATEGORIES(); PROPERTYDEFS [] propertyDefs = meridio_.getStaticData().getPROPERTYDEFS(); for (int i = 0; i < propertyDefs.length; i++) { if (propertyDefs[i].getTableName() == null) { continue; } if (propertyDefs[i].getTableName().compareTo("DOCUMENTS") == 0) { meridioDocumentProperties.add(propertyDefs[i].getDisplayName()); } if ( (propertyDefs[i].getCategoryId() == 4 || // Global Document Category propertyDefs[i].getCategoryId() == 5 || // Mail Message propertyDefs[i].getCategoryId() > 100) && // Custom Category propertyDefs[i].getTableName().compareTo("DOCUMENT_CUSTOMPROPS") == 0) { for (int j = 0; j < categories.length; j++) { if (categories[j].getPROP_categoryId() == propertyDefs[i].getCategoryId()) { meridioDocumentProperties.add(categories[j].getPROP_title() + "." + propertyDefs[i].getDisplayName()); Logging.connectors.debug("Prop: <" + categories[j].getPROP_title() + "." + propertyDefs[i].getDisplayName() + "> Column <" + propertyDefs[i].getColumnName() + ">"); break; } } } } String [] returnStringArray = new String[meridioDocumentProperties.size()]; Iterator it = meridioDocumentProperties.iterator(); for (int i = 0; it.hasNext(); i++) { returnStringArray[i] = (String) it.next(); } java.util.Arrays.sort(returnStringArray); Logging.connectors.debug("Exiting 'getMeridioDocumentProperties' method"); return returnStringArray; } catch (org.apache.axis.AxisFault e) { long currentTime = System.currentTimeMillis(); if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://xml.apache.org/axis/","HTTP"))) { org.w3c.dom.Element elem = e.lookupFaultDetail(new javax.xml.namespace.QName("http://xml.apache.org/axis/","HttpErrorCode")); if (elem != null) { elem.normalize(); String httpErrorCode = elem.getFirstChild().getNodeValue().trim(); throw new ManifoldCFException("Unexpected http error code "+httpErrorCode+" getting document properties: "+e.getMessage()); } throw new ManifoldCFException("Unknown http error occurred while getting document properties: "+e.getMessage(),e); } if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://schemas.xmlsoap.org/soap/envelope/","Server.userException"))) { String exceptionName = e.getFaultString(); if (exceptionName.equals("java.lang.InterruptedException")) throw new ManifoldCFException("Interrupted",ManifoldCFException.INTERRUPTED); } if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://schemas.xmlsoap.org/soap/envelope/","Server"))) { if (e.getFaultString().indexOf(" 23031#") != -1) { // This means that the session has expired, so reset it and retry meridio_ = null; continue; } } throw new ManifoldCFException("Meridio: Got an unknown remote exception getting document properties - axis fault = "+e.getFaultCode().getLocalPart()+", detail = "+e.getFaultString(),e); } catch (RemoteException remoteException) { throw new ManifoldCFException("Meridio: A Remote Exception occurred while " + "retrieving the Meridio document properties: "+remoteException.getMessage(), remoteException); } catch (MeridioDataSetException meridioDataSetException) { throw new ManifoldCFException("Meridio: DataSet Exception occurred retrieving the Meridio document properties: "+meridioDataSetException.getMessage(), meridioDataSetException); } } } public MeridioClassContents [] getClassOrFolderContents ( int classOrFolderId ) throws ManifoldCFException, ServiceInterruption { Logging.connectors.debug("Entering 'getClassOrFolderContents' method"); while (true) { getSession(); ArrayList meridioContainers = new ArrayList(); try { RMDataSet ds = meridio_.getClassContents(classOrFolderId, false, false, false); if (ds == null) { Logging.connectors.debug("No classes or folders in returned DataSet"); return new MeridioClassContents [] {}; } Rm2vClass [] classes = ds.getRm2vClass(); Rm2vFolder [] folders = ds.getRm2vFolder(); for (int i = 0; i < classes.length; i++) { if (classes[i].getHomePage() == null || classes[i].getHomePage().length() == 0) // Not a federated link { MeridioClassContents classContents = new MeridioClassContents(); classContents.containerType = MeridioClassContents.CLASS; classContents.classOrFolderId = classes[i].getId(); classContents.classOrFolderName = classes[i].getName(); meridioContainers.add(classContents); } } for (int i = 0; i < folders.length; i++) { MeridioClassContents classContents = new MeridioClassContents(); classContents.containerType = MeridioClassContents.FOLDER; classContents.classOrFolderId = folders[i].getId(); classContents.classOrFolderName = folders[i].getName(); meridioContainers.add(classContents); } MeridioClassContents [] classArray = new MeridioClassContents[meridioContainers.size()]; Iterator it = meridioContainers.iterator(); for (int i = 0; it.hasNext(); i++) { classArray[i] = (MeridioClassContents) it.next(); } Logging.connectors.debug("Exiting 'getClassOrFolderContents' method"); return classArray; } catch (org.apache.axis.AxisFault e) { long currentTime = System.currentTimeMillis(); if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://xml.apache.org/axis/","HTTP"))) { org.w3c.dom.Element elem = e.lookupFaultDetail(new javax.xml.namespace.QName("http://xml.apache.org/axis/","HttpErrorCode")); if (elem != null) { elem.normalize(); String httpErrorCode = elem.getFirstChild().getNodeValue().trim(); throw new ManifoldCFException("Unexpected http error code "+httpErrorCode+" getting class or folder contents: "+e.getMessage()); } throw new ManifoldCFException("Unknown http error occurred while getting class or folder contents: "+e.getMessage(),e); } if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://schemas.xmlsoap.org/soap/envelope/","Server.userException"))) { String exceptionName = e.getFaultString(); if (exceptionName.equals("java.lang.InterruptedException")) throw new ManifoldCFException("Interrupted",ManifoldCFException.INTERRUPTED); } if (e.getFaultCode().equals(new javax.xml.namespace.QName("http://schemas.xmlsoap.org/soap/envelope/","Server"))) { if (e.getFaultString().indexOf(" 23031#") != -1) { // This means that the session has expired, so reset it and retry meridio_ = null; continue; } } throw new ManifoldCFException("Meridio: Got an unknown remote exception getting class or folder contents - axis fault = "+e.getFaultCode().getLocalPart()+", detail = "+e.getFaultString(),e); } catch (RemoteException remoteException) { throw new ManifoldCFException("Meridio: A Remote Exception occurred while " + "retrieving class or folder contents: "+remoteException.getMessage(), remoteException); } catch (MeridioDataSetException meridioDataSetException) { throw new ManifoldCFException("Meridio: A problem occurred manipulating the Web " + "Service XML: "+meridioDataSetException.getMessage(), meridioDataSetException); } } } /** Helper class for keeping track of metadata index for each document */ protected static class MutableInteger { int value = 0; public MutableInteger() { } public int getValue() { return value; } public void increment() { value++; } } }