// Copyright 2007 Google Inc.
//
// Licensed 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 com.google.enterprise.connector.otex.client.lapi;
import com.google.enterprise.connector.otex.client.Client;
import com.google.enterprise.connector.otex.client.ClientFactory;
import com.opentext.api.LLNameEnumeration;
import com.opentext.api.LLSession;
import com.opentext.api.LLValue;
import java.io.File;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A direct LAPI client factory implementation.
*/
public final class LapiClientFactory implements ClientFactory {
/** The logger for this class. */
private static final Logger LOGGER =
Logger.getLogger(LapiClientFactory.class.getName());
private String server = null;
private int port = 0;
private String connection = null;
private String username = null;
private String password = null;
private LLValue config = null;
private boolean useUsernamePasswordWithWebServer = false;
/** {@inheritDoc} */
public void setServer(String value) {
server = value;
}
/** {@inheritDoc} */
public void setPort(int value) {
port = value;
}
/** {@inheritDoc} */
public void setConnection(String value) {
connection = value;
}
/** {@inheritDoc} */
public void setUsername(String value) {
username = value;
}
/** {@inheritDoc} */
public void setPassword(String value) {
password = value;
}
/** {@inheritDoc} */
public void setDomainName(String value) {
setConfig("DomainName", value);
}
/** {@inheritDoc} */
public void setEncoding(String value) {
setConfig("encoding", value);
}
/** {@inheritDoc} */
public void setLivelinkCgi(String value) {
setConfig("LivelinkCGI", value);
}
/** {@inheritDoc} */
public void setHttps(boolean value) {
// We've treated this as a boolean, but LAPI wants an integer.
setConfig("HTTPS", value ? 1 : 0);
}
/** {@inheritDoc} */
public void setHttpUsername(String value) {
setConfig("HTTPUserName", value);
}
/** {@inheritDoc} */
public void setHttpPassword(String value) {
setConfig("HTTPPassword", value);
}
/** {@inheritDoc} */
public void setVerifyServer(boolean value) {
setConfig("VerifyServer", value ? 1 : 0);
}
/** {@inheritDoc} */
/* TODO: For the beta release, with the connector managers
* running outside the GSA, support the use of directories as
* entries in the caRootCerts property. In the future, it
* might need to be a URL to a location from which we can
* read the certificates, write them to a file, then read
* them, since the admin may not have generic filesystem
* access to the GSA if the connectors are running there.
*/
public void setCaRootCerts(List<String> value) {
LLValue list = new LLValue().setList();
for (int i = 0; i < value.size(); i++) {
String certEntry = value.get(i);
// If the config form property is empty, and a
// directory is provided in connectorInstance.xml
// (making the list non-empty), skip the empty
// string. I don't think it's possible to get a null
// value in the list, but it won't hurt to check.
if (certEntry == null || certEntry.length() == 0)
continue;
File certDir = new File(certEntry);
if (certDir.exists() && certDir.isDirectory()) {
if (LOGGER.isLoggable(Level.CONFIG)) {
LOGGER.config("Reading certificates from " +
certDir.getAbsolutePath());
}
LLValue rootCACertsList = new LLValue();
LLSession.GetCARootCerts(certDir.getAbsolutePath(), rootCACertsList);
int size = rootCACertsList.size();
if (LOGGER.isLoggable(Level.CONFIG))
LOGGER.config("Found " + size + " certificates");
for (int j = 0; j < size; j++)
list.add(rootCACertsList.toValue(j));
}
else {
LLValue cert = new LLValue().setString(certEntry);
list.add(cert);
}
}
setConfig("CARootCerts", list);
}
/** {@inheritDoc} */
public void setEnableNtlm(boolean value) {
setConfig("EnableNTLM", value ? 1 : 0);
}
/** {@inheritDoc} */
public void setUseUsernamePasswordWithWebServer(boolean value) {
useUsernamePasswordWithWebServer = value;
}
/**
* Sets a string feature in the config assoc.
*
* @param name the feature name
* @param value the feature value
*/
/*
* This method is synchronized to emphasize safety over speed. It
* is extremely unlikely that this instance will be called from
* multiple threads, but this is also a very fast, rarely executed
* operation, so there's no need to cut corners.
*/
private synchronized void setConfig(String name, String value) {
if (config == null)
config = (new LLValue()).setAssocNotSet();
config.add(name, value);
}
/**
* Sets an integer feature in the config assoc.
*
* @param name the feature name
* @param value the feature value
*/
private synchronized void setConfig(String name, int value) {
if (config == null)
config = (new LLValue()).setAssocNotSet();
config.add(name, value);
}
/**
* Sets an LLValue feature in the config assoc.
*
* @param name the feature name
* @param value the feature value
*/
private synchronized void setConfig(String name, LLValue value) {
if (config == null)
config = (new LLValue()).setAssocNotSet();
config.add(name, value);
}
/** {@inheritDoc} */
public Client createClient() {
// Construct a new LLSession and pass that to the client, just
// to avoid having to pass all of the constructor arguments to
// LapiClient.
logProperties(username, password, config);
LLSession session = new LLSession(server, port, connection, username,
password, config);
return new LapiClient(session);
}
/**
* {@inheritDoc}
*
* <p>
* Construct a new LLSession using the provided username
* and password instead of the configured ones. This is
* intended for use by the AuthenticationManager.
*/
/*
* If a separate set of authentication parameters was not
* specified, the single LapiClientFactory instance used for
* crawling will also be used for authentication. When it's
* used for authentication, we want to be sure that no
* crawling-related identity information is used. There are
* two places a username/password can be specified: the
* top-level ones, and the HTTPUserName and HTTPPassword
* values in the config object. The only way to tell if the
* config parameters are present is to try to read them and
* let LAPI thrown an exception; there's no "IsFeature" in
* the Java LAPI client. Rather than work to see if they're
* present, we'll just copy the config and ensure that
* they're not.
*
* FIXME: Can the username/password ever be null?
*
* XXX: We are intentionally shadowing the username and password fields
* in this method to prevent their accidental use.
*/
public Client createClient(String username, String password) {
// Copy the config.
LLValue localConfig = null;
if (config != null) {
localConfig = new LLValue().setAssocNotSet();
LLNameEnumeration names = config.enumerateNames();
while (names.hasMoreElements()) {
String name = names.nextElement().toString();
if ("HTTPUserName".equals(name) || "HTTPPassword".equals(name)) {
continue;
}
localConfig.add(name, config.toValue(name));
}
}
// Add the web credentials if needed.
if (localConfig != null && useUsernamePasswordWithWebServer) {
localConfig.add("HTTPUserName", username);
localConfig.add("HTTPPassword", password);
}
logProperties(username, password, localConfig);
LLSession session = new LLSession(server, port, connection,
username, password, localConfig);
return new LapiClient(session);
}
/**
* Logs the properties to be used for an LLSession.
*
* @param currentUsername the username to log
* @param currentPassword the password to log
* @param currentConfig the LLSession extra parameters to log
*/
private void logProperties(String currentUsername,
String currentPassword, LLValue currentConfig) {
if (LOGGER.isLoggable(Level.FINER)) {
StringBuilder configString = new StringBuilder();
if (currentConfig == null)
configString.append("config=null");
else {
configString.append("config={");
LLNameEnumeration names = currentConfig.enumerateNames();
while (names.hasMoreElements()) {
String name = names.nextElement().toString();
configString.append(name).append("=");
if (name.endsWith("assword"))
configString.append("[...]");
else
configString.append(currentConfig.toString(name));
configString.append("; ");
}
configString.append("}");
}
LOGGER.finer("server=" + server + "; port=" + port +
"; connection=" + connection + "; username=" + currentUsername +
"; password=[...]; useUsernameForWebServer: " +
useUsernamePasswordWithWebServer + "; " + configString);
}
}
}