Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.tirasa.connid.commons.scripted.AbstractScriptedConnector Maven / Gradle / Ivy
/*
* ====================
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2016 ConnId. All rights reserved.
*
* The contents of this file are subject to the terms of the Common Development
* and Distribution License("CDDL") (the "License"). You may not use this file
* except in compliance with the License.
*
* You can obtain a copy of the License at
* http://opensource.org/licenses/cddl1.php
* See the License for the specific language governing permissions and limitations
* under the License.
*
* When distributing the Covered Code, include this CDDL Header Notice in each file
* and include the License file at http://opensource.org/licenses/cddl1.php.
* If applicable, add the following below this CDDL Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
* ====================
*/
package net.tirasa.connid.commons.scripted;
import static net.tirasa.connid.commons.scripted.Constants.MSG_OBJECT_CLASS_REQUIRED;
import static net.tirasa.connid.commons.scripted.Constants.MSG_INVALID_ATTRIBUTE_SET;
import static net.tirasa.connid.commons.scripted.Constants.MSG_BLANK_UID;
import static net.tirasa.connid.commons.scripted.Constants.MSG_BLANK_RESULT_HANDLER;
import static net.tirasa.connid.commons.scripted.Constants.MSG_INVALID_SCRIPT;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.identityconnectors.common.CollectionUtil;
import org.identityconnectors.common.IOUtil;
import org.identityconnectors.common.StringUtil;
import org.identityconnectors.common.logging.Log;
import org.identityconnectors.common.script.ScriptExecutor;
import org.identityconnectors.common.script.ScriptExecutorFactory;
import org.identityconnectors.common.security.GuardedString;
import org.identityconnectors.common.security.SecurityUtil;
import org.identityconnectors.framework.api.operations.ResolveUsernameApiOp;
import org.identityconnectors.framework.common.FrameworkUtil;
import org.identityconnectors.framework.common.exceptions.ConnectorException;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.AttributeBuilder;
import org.identityconnectors.framework.common.objects.AttributeDelta;
import org.identityconnectors.framework.common.objects.AttributeUtil;
import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder;
import org.identityconnectors.framework.common.objects.Name;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.OperationalAttributes;
import org.identityconnectors.framework.common.objects.ResultsHandler;
import org.identityconnectors.framework.common.objects.Schema;
import org.identityconnectors.framework.common.objects.SchemaBuilder;
import org.identityconnectors.framework.common.objects.ScriptContext;
import org.identityconnectors.framework.common.objects.SearchResult;
import org.identityconnectors.framework.common.objects.SyncDeltaBuilder;
import org.identityconnectors.framework.common.objects.SyncDeltaType;
import org.identityconnectors.framework.common.objects.SyncResultsHandler;
import org.identityconnectors.framework.common.objects.SyncToken;
import org.identityconnectors.framework.common.objects.Uid;
import org.identityconnectors.framework.spi.Configuration;
import org.identityconnectors.framework.spi.Connector;
import org.identityconnectors.framework.spi.SearchResultsHandler;
import org.identityconnectors.framework.spi.operations.AuthenticateOp;
import org.identityconnectors.framework.spi.operations.CreateOp;
import org.identityconnectors.framework.spi.operations.DeleteOp;
import org.identityconnectors.framework.spi.operations.SchemaOp;
import org.identityconnectors.framework.spi.operations.ScriptOnConnectorOp;
import org.identityconnectors.framework.spi.operations.SearchOp;
import org.identityconnectors.framework.spi.operations.SyncOp;
import org.identityconnectors.framework.spi.operations.TestOp;
import org.identityconnectors.framework.spi.operations.UpdateAttributeValuesOp;
import org.identityconnectors.framework.spi.operations.UpdateDeltaOp;
import org.identityconnectors.framework.spi.operations.UpdateOp;
public abstract class AbstractScriptedConnector implements Connector,
CreateOp, UpdateOp, UpdateDeltaOp, UpdateAttributeValuesOp, DeleteOp, AuthenticateOp,
ResolveUsernameApiOp, SchemaOp, SyncOp, TestOp, SearchOp>, ScriptOnConnectorOp {
protected static final Log LOG = Log.getLog(AbstractScriptedConnector.class);
private static final Pattern VARIABLE = Pattern.compile("\\$\\{[a-zA-Z]+\\w*\\}");
public static final String resolveVariables(final String input) {
Set vars = new HashSet<>();
Matcher matcher = VARIABLE.matcher(input);
while (matcher.find()) {
int n = 0;
for (int i = matcher.start() - 1; i >= 0 && input.charAt(i) == '\\'; i--) {
n++;
}
if (n % 2 != 0) {
continue;
}
vars.add(input.substring(matcher.start() + 2, matcher.end() - 1));
}
String resolved = input;
for (String var : vars) {
String replacement = System.getProperty(var);
if (replacement != null) {
resolved = resolved.replace("${" + var + "}", replacement);
}
}
return resolved;
}
protected C config;
private Schema schema;
private ScriptExecutorFactory factory;
private ScriptExecutor createExecutor;
private ScriptExecutor updateExecutor;
private ScriptExecutor deleteExecutor;
private ScriptExecutor searchExecutor;
private ScriptExecutor authenticateExecutor;
private ScriptExecutor resolveUsernameExecutor;
private ScriptExecutor syncExecutor;
private ScriptExecutor schemaExecutor;
private ScriptExecutor testExecutor;
private ScriptExecutor runOnConnectorExecutor;
@Override
public C getConfiguration() {
return config;
}
@Override
public void dispose() {
// nothing to do
}
private ScriptExecutor getScriptExecutor(String script, String scriptFileName) {
String scriptCode = script;
ScriptExecutor scriptExec = null;
try {
if (scriptFileName != null) {
scriptCode = IOUtil.readFileUTF8(new File(resolveVariables(scriptFileName)));
}
if (scriptCode.length() > 0) {
scriptExec = factory.newScriptExecutor(getClass().getClassLoader(), scriptCode, true);
}
} catch (IOException e) {
throw new ConnectorException("Script error", e);
}
return scriptExec;
}
@Override
@SuppressWarnings("unchecked")
public void init(final Configuration cfg) {
this.config = (C) cfg;
this.factory = ScriptExecutorFactory.newInstance(config.getScriptingLanguage());
// We need an executor for each and every script. At least, they'll get evaluated and compiled.
// We privilege the script file over the script string if script filename is null, then we use the script string
if (checkReloadScript(createExecutor, config.getCreateScript(), config.getCreateScriptFileName())) {
createExecutor = getScriptExecutor(config.getCreateScript(), config.getCreateScriptFileName());
LOG.ok("Create script loaded");
}
if (checkReloadScript(updateExecutor, config.getUpdateScript(), config.getUpdateScriptFileName())) {
updateExecutor = getScriptExecutor(config.getUpdateScript(), config.getUpdateScriptFileName());
LOG.ok("Update script loaded");
}
if (checkReloadScript(deleteExecutor, config.getDeleteScript(), config.getDeleteScriptFileName())) {
deleteExecutor = getScriptExecutor(config.getDeleteScript(), config.getDeleteScriptFileName());
LOG.ok("Delete script loaded");
}
if (checkReloadScript(searchExecutor, config.getSearchScript(), config.getSearchScriptFileName())) {
searchExecutor = getScriptExecutor(config.getSearchScript(), config.getSearchScriptFileName());
LOG.ok("Search script loaded");
}
if (checkReloadScript(authenticateExecutor, config.getAuthenticateScript(), config.
getAuthenticateScriptFileName())) {
authenticateExecutor = getScriptExecutor(
config.getAuthenticateScript(), config.getAuthenticateScriptFileName());
LOG.ok("Authenticate script loaded");
}
if (checkReloadScript(resolveUsernameExecutor, config.getResolveUsernameScript(), config.
getResolveUsernameScriptFileName())) {
resolveUsernameExecutor = getScriptExecutor(
config.getResolveUsernameScript(), config.getResolveUsernameScriptFileName());
LOG.ok("ResolveUsername script loaded");
}
if (checkReloadScript(syncExecutor, config.getSyncScript(), config.getSyncScriptFileName())) {
syncExecutor = getScriptExecutor(config.getSyncScript(), config.getSyncScriptFileName());
LOG.ok("Sync script loaded");
}
if (checkReloadScript(schemaExecutor, config.getSchemaScript(), config.getSchemaScriptFileName())) {
schemaExecutor = getScriptExecutor(config.getSchemaScript(), config.getSchemaScriptFileName());
LOG.ok("Schema script loaded");
}
if (checkReloadScript(testExecutor, config.getTestScript(), config.getTestScriptFileName())) {
testExecutor = getScriptExecutor(config.getTestScript(), config.getTestScriptFileName());
LOG.ok("Test script loaded");
}
LOG.ok("Connector {0} successfully inited", getClass().getName());
}
protected abstract Map buildArguments();
@Override
public Uid create(
final ObjectClass objectClass,
final Set createAttributes,
final OperationOptions options) {
if (config.isReloadScriptOnExecution()) {
createExecutor = getScriptExecutor(config.getCreateScript(), config.getCreateScriptFileName());
LOG.ok("Create script loaded");
}
if (createExecutor != null) {
if (objectClass == null) {
throw new IllegalArgumentException(config.getMessage(MSG_OBJECT_CLASS_REQUIRED));
}
LOG.ok("Object class: {0}", objectClass.getObjectClassValue());
if (createAttributes == null || createAttributes.isEmpty()) {
throw new IllegalArgumentException(config.getMessage(MSG_INVALID_ATTRIBUTE_SET));
}
Map arguments = buildArguments();
arguments.put("action", "CREATE");
arguments.put("log", LOG);
arguments.put("objectClass", objectClass.getObjectClassValue());
arguments.put("options", options.getOptions());
// We give the id (name) as an argument, more friendly than dealing with __NAME__
String id = null;
Name name = AttributeUtil.getNameFromAttributes(createAttributes);
if (name == null) {
Uid uid = AttributeUtil.getUidAttribute(createAttributes);
if (uid != null) {
id = uid.getUidValue();
}
} else {
id = name.getNameValue();
}
arguments.put("id", id);
Map> attrMap = new HashMap<>();
for (Attribute attr : createAttributes) {
attrMap.put(attr.getName(), attr.getValue());
}
// let's get rid of __NAME__
attrMap.remove(Name.NAME);
arguments.put("attributes", attrMap);
// Password - if allowed we provide it in clear
if (config.getClearTextPasswordToScript()) {
GuardedString gpasswd = AttributeUtil.getPasswordValue(createAttributes);
arguments.put("password", gpasswd == null ? null : SecurityUtil.decrypt(gpasswd));
}
try {
Object uidAfter = createExecutor.execute(arguments);
if (uidAfter instanceof String) {
LOG.ok("{0} created", uidAfter);
return new Uid((String) uidAfter);
} else {
throw new ConnectorException("Create script didn't return with the __UID__ value");
}
} catch (Exception e) {
throw new ConnectorException("Create script error", e);
}
} else {
throw new UnsupportedOperationException(config.getMessage(MSG_INVALID_SCRIPT));
}
}
private Map attributes2Arguments(final boolean plainUpdate, final Set attrs) {
if (CollectionUtil.isEmpty(attrs)) {
throw new IllegalArgumentException(config.getMessage(MSG_INVALID_ATTRIBUTE_SET));
}
Map> attrMap = new HashMap<>();
for (Attribute attr : attrs) {
if (OperationalAttributes.isOperationalAttribute(attr)) {
if (plainUpdate) {
attrMap.put(attr.getName(), attr.getValue());
}
} else {
attrMap.put(attr.getName(), attr.getValue());
}
}
Map arguments = new HashMap<>();
arguments.put("attributes", attrMap);
// Do we need to update the password?
if (config.getClearTextPasswordToScript() && plainUpdate) {
GuardedString gpasswd = AttributeUtil.getPasswordValue(attrs);
arguments.put("password", gpasswd == null ? null : SecurityUtil.decrypt(gpasswd));
}
return arguments;
}
private Uid genericUpdate(
final String method,
final ObjectClass objectClass,
final Uid uid,
final Map additionalArguments,
final OperationOptions options) {
if (config.isReloadScriptOnExecution()) {
updateExecutor = getScriptExecutor(config.getUpdateScript(), config.getUpdateScriptFileName());
LOG.ok("Update ({0}) script loaded", method);
}
if (updateExecutor != null) {
if (objectClass == null) {
throw new IllegalArgumentException(config.getMessage(MSG_OBJECT_CLASS_REQUIRED));
}
LOG.ok("Object class: {0}", objectClass.getObjectClassValue());
if (uid == null || uid.getUidValue() == null) {
throw new IllegalArgumentException(config.getMessage(MSG_BLANK_UID));
}
String id = uid.getUidValue();
Map arguments = buildArguments();
arguments.putAll(additionalArguments);
arguments.put("action", method);
arguments.put("log", LOG);
arguments.put("objectClass", objectClass.getObjectClassValue());
arguments.put("uid", id);
arguments.put("options", options.getOptions());
try {
Object uidAfter = updateExecutor.execute(arguments);
if (uidAfter instanceof String) {
LOG.ok("{0} updated ({1})", uidAfter, method);
return new Uid((String) uidAfter);
}
} catch (Exception e) {
throw new ConnectorException("Update(" + method + ") script error", e);
}
throw new ConnectorException("Update script didn't return with the __UID__ value");
} else {
throw new UnsupportedOperationException(config.getMessage(MSG_INVALID_SCRIPT));
}
}
@Override
public Uid update(
final ObjectClass objectClass,
final Uid uid,
final Set replaceAttributes,
final OperationOptions options) {
return genericUpdate("UPDATE", objectClass, uid, attributes2Arguments(true, replaceAttributes), options);
}
@Override
public Set updateDelta(
final ObjectClass objectClass,
final Uid uid,
final Set modifications,
final OperationOptions options) {
if (CollectionUtil.isEmpty(modifications)) {
throw new IllegalArgumentException(config.getMessage(MSG_INVALID_ATTRIBUTE_SET));
}
Map> valuesToAdd = new HashMap<>();
Map> valuesToRemove = new HashMap<>();
Map> valuesToReplace = new HashMap<>();
for (AttributeDelta attr : modifications) {
valuesToAdd.put(attr.getName(), attr.getValuesToAdd());
valuesToRemove.put(attr.getName(), attr.getValuesToRemove());
valuesToReplace.put(attr.getName(), attr.getValuesToReplace());
}
Map arguments = new HashMap<>();
arguments.put("valuesToAdd", valuesToAdd);
arguments.put("valuesToRemove", valuesToRemove);
arguments.put("valuesToReplace", valuesToReplace);
genericUpdate("UPDATE_DELTA", objectClass, uid, arguments, options);
return modifications;
}
@Override
public Uid addAttributeValues(
final ObjectClass objectClass,
final Uid uid,
final Set valuesToAdd,
final OperationOptions options) {
return genericUpdate("ADD_ATTRIBUTE_VALUES",
objectClass, uid, attributes2Arguments(false, valuesToAdd), options);
}
@Override
public Uid removeAttributeValues(
final ObjectClass objectClass,
final Uid uid, Set valuesToRemove,
final OperationOptions options) {
return genericUpdate("REMOVE_ATTRIBUTE_VALUES",
objectClass, uid, attributes2Arguments(false, valuesToRemove), options);
}
@Override
public void delete(
final ObjectClass objectClass,
final Uid uid,
final OperationOptions options) {
if (config.isReloadScriptOnExecution()) {
deleteExecutor = getScriptExecutor(config.getDeleteScript(), config.getDeleteScriptFileName());
LOG.ok("Delete script loaded");
}
if (deleteExecutor != null) {
if (objectClass == null) {
throw new IllegalArgumentException(config.getMessage(MSG_OBJECT_CLASS_REQUIRED));
}
LOG.ok("Object class: {0}", objectClass.getObjectClassValue());
if (uid == null || uid.getUidValue() == null) {
throw new IllegalArgumentException(config.getMessage(MSG_BLANK_UID));
}
String id = uid.getUidValue();
Map arguments = buildArguments();
arguments.put("action", "DELETE");
arguments.put("log", LOG);
arguments.put("objectClass", objectClass.getObjectClassValue());
arguments.put("uid", id);
arguments.put("options", options.getOptions());
try {
deleteExecutor.execute(arguments);
LOG.ok("{0} deleted", id);
} catch (Exception e) {
throw new ConnectorException("Delete script error", e);
}
} else {
throw new UnsupportedOperationException(config.getMessage(MSG_INVALID_SCRIPT));
}
}
@Override
public Uid authenticate(
final ObjectClass objectClass,
final String username,
final GuardedString password,
final OperationOptions options) {
if (config.isReloadScriptOnExecution()) {
authenticateExecutor = getScriptExecutor(
config.getAuthenticateScript(), config.getAuthenticateScriptFileName());
LOG.ok("Authenticate script loaded");
}
if (authenticateExecutor != null) {
if (objectClass == null) {
throw new IllegalArgumentException(config.getMessage(MSG_OBJECT_CLASS_REQUIRED));
}
LOG.ok("Object class: {0}", objectClass.getObjectClassValue());
Map arguments = buildArguments();
arguments.put("action", "AUTHENTICATE");
arguments.put("log", LOG);
arguments.put("objectClass", objectClass.getObjectClassValue());
arguments.put("username", username);
arguments.put("password",
config.getClearTextPasswordToScript() ? SecurityUtil.decrypt(password) : password);
arguments.put("options", options.getOptions());
try {
Object uid = authenticateExecutor.execute(arguments);
if (uid instanceof String) {
LOG.ok("{0} authenticated", uid);
return new Uid((String) uid);
}
} catch (Exception e) {
throw new ConnectorException("Authenticate script error", e);
}
throw new ConnectorException("Authenticate script didn't return with the __UID__ value");
} else {
throw new UnsupportedOperationException(config.getMessage(MSG_INVALID_SCRIPT));
}
}
@Override
public Uid resolveUsername(
final ObjectClass objectClass,
final String username,
final OperationOptions options) {
if (config.isReloadScriptOnExecution()) {
resolveUsernameExecutor = getScriptExecutor(
config.getResolveUsernameScript(), config.getResolveUsernameScriptFileName());
LOG.ok("ResolveUsername script loaded");
}
if (resolveUsernameExecutor != null) {
if (objectClass == null) {
throw new IllegalArgumentException(config.getMessage(MSG_OBJECT_CLASS_REQUIRED));
}
LOG.ok("Object class: {0}", objectClass.getObjectClassValue());
Map arguments = buildArguments();
arguments.put("action", "RESOLVE USERNAME");
arguments.put("log", LOG);
arguments.put("objectClass", objectClass.getObjectClassValue());
arguments.put("username", username);
arguments.put("options", options.getOptions());
try {
Object uid = resolveUsernameExecutor.execute(arguments);
if (uid instanceof String) {
LOG.ok("{0} resolved", uid);
return new Uid((String) uid);
}
} catch (Exception e) {
throw new ConnectorException("ResolveUsername script error", e);
}
throw new ConnectorException("ResolveUsername script didn't return with the __UID__ value");
} else {
throw new UnsupportedOperationException(config.getMessage(MSG_INVALID_SCRIPT));
}
}
@Override
public Schema schema() {
SchemaBuilder scmb = new SchemaBuilder(getClass());
if (config.isReloadScriptOnExecution()) {
schemaExecutor = getScriptExecutor("", config.getSchemaScriptFileName());
}
if (schemaExecutor != null) {
Map arguments = buildArguments();
arguments.put("action", "SCHEMA");
arguments.put("log", LOG);
arguments.put("builder", scmb);
try {
schemaExecutor.execute(arguments);
} catch (Exception e) {
throw new ConnectorException("Schema script error", e);
}
} else {
throw new UnsupportedOperationException("SCHEMA script executor is null. Problem loading Schema script");
}
schema = scmb.build();
return schema;
}
@Override
public void executeQuery(
final ObjectClass objectClass,
final Map query,
final ResultsHandler handler,
final OperationOptions options) {
if (config.isReloadScriptOnExecution()) {
searchExecutor = getScriptExecutor(config.getSearchScript(), config.getSearchScriptFileName());
LOG.ok("Search script loaded");
}
if (searchExecutor != null) {
if (objectClass == null) {
throw new IllegalArgumentException(config.getMessage(MSG_OBJECT_CLASS_REQUIRED));
}
LOG.ok("ObjectClass: {0}", objectClass.getObjectClassValue());
if (handler == null) {
throw new IllegalArgumentException(config.getMessage(MSG_BLANK_RESULT_HANDLER));
}
Map arguments = buildArguments();
arguments.put("objectClass", objectClass.getObjectClassValue());
arguments.put("action", "SEARCH");
arguments.put("log", LOG);
arguments.put("options", options.getOptions());
arguments.put("query", query);
try {
@SuppressWarnings("unchecked")
List> results = (List>) searchExecutor.execute(arguments);
LOG.ok("Search ok");
processResults(objectClass, results, handler);
} catch (Exception e) {
throw new ConnectorException("Search script error", e);
}
} else {
throw new UnsupportedOperationException(config.getMessage(MSG_INVALID_SCRIPT));
}
}
@Override
public void sync(
final ObjectClass objectClass,
final SyncToken token,
final SyncResultsHandler handler,
final OperationOptions options) {
if (config.isReloadScriptOnExecution()) {
syncExecutor = getScriptExecutor(config.getSyncScript(), config.getSyncScriptFileName());
LOG.ok("Sync script loaded");
}
if (syncExecutor != null) {
if (objectClass == null) {
throw new IllegalArgumentException(config.getMessage(MSG_OBJECT_CLASS_REQUIRED));
}
LOG.ok("ObjectClass: {0}", objectClass.getObjectClassValue());
if (handler == null) {
throw new IllegalArgumentException(config.getMessage(MSG_BLANK_RESULT_HANDLER));
}
Map arguments = buildArguments();
arguments.put("objectClass", objectClass.getObjectClassValue());
arguments.put("action", "SYNC");
arguments.put("log", LOG);
arguments.put("options", options.getOptions());
arguments.put("token", token == null ? null : token.getValue());
try {
@SuppressWarnings("unchecked")
List> results = (List>) syncExecutor.execute(arguments);
LOG.ok("Sync ok");
processDeltas(objectClass, results, handler);
} catch (Exception e) {
throw new ConnectorException("Sync script error", e);
}
} else {
throw new UnsupportedOperationException(config.getMessage(MSG_INVALID_SCRIPT));
}
}
@Override
public SyncToken getLatestSyncToken(final ObjectClass objectClass) {
if (config.isReloadScriptOnExecution()) {
syncExecutor = getScriptExecutor(config.getSyncScript(), config.getSyncScriptFileName());
LOG.ok("Sync script loaded");
}
if (syncExecutor != null) {
SyncToken st = null;
if (objectClass == null) {
throw new IllegalArgumentException(config.getMessage(MSG_OBJECT_CLASS_REQUIRED));
}
LOG.ok("ObjectClass: {0}", objectClass.getObjectClassValue());
Map arguments = buildArguments();
arguments.put("objectClass", objectClass.getObjectClassValue());
arguments.put("action", "GET_LATEST_SYNC_TOKEN");
arguments.put("log", LOG);
try {
// We expect the script to return a value (or null) that makes the sync token
// !! result has to be one of the framework known types...
Object result = syncExecutor.execute(arguments);
LOG.ok("GetLatestSyncToken ok");
FrameworkUtil.checkAttributeType(result.getClass());
st = new SyncToken(result);
} catch (java.lang.IllegalArgumentException ae) {
throw new ConnectorException("Unknown Token type", ae);
} catch (Exception e) {
throw new ConnectorException("Sync (GetLatestSyncToken) script error", e);
}
return st;
} else {
throw new UnsupportedOperationException(config.getMessage(MSG_INVALID_SCRIPT));
}
}
@Override
public Object runScriptOnConnector(final ScriptContext request, final OperationOptions options) {
Object result = null;
try {
if (request.getScriptText() != null && request.getScriptText().length() > 0) {
assert request.getScriptLanguage().equalsIgnoreCase(config.getScriptingLanguage());
runOnConnectorExecutor = factory.newScriptExecutor(
getClass().getClassLoader(), request.getScriptText(), true);
}
} catch (Exception e) {
throw new ConnectorException("RunOnConnector script parse error", e);
}
if (runOnConnectorExecutor != null) {
Map arguments = buildArguments();
arguments.put("action", "RUNSCRIPTONCONNECTOR");
arguments.put("log", LOG);
arguments.put("options", options.getOptions());
arguments.put("scriptsArguments", request.getScriptArguments());
try {
// We return any object from the script
result = runOnConnectorExecutor.execute(arguments);
LOG.ok("runOnConnector script ok");
} catch (Exception e) {
throw new ConnectorException("runOnConnector script error", e);
}
return result;
} else {
throw new UnsupportedOperationException(config.getMessage(MSG_INVALID_SCRIPT));
}
}
@Override
public void test() {
config.validate();
if (config.isReloadScriptOnExecution()) {
testExecutor = getScriptExecutor(config.getTestScript(), config.getTestScriptFileName());
LOG.ok("Test script loaded");
}
if (testExecutor != null) {
Map arguments = buildArguments();
arguments.put("action", "TEST");
arguments.put("log", LOG);
try {
testExecutor.execute(arguments);
LOG.ok("Test ok");
} catch (Exception e) {
throw new ConnectorException("Test script error", e);
}
}
}
private void processResults(
final ObjectClass objectClass,
final List> results,
final ResultsHandler handler) {
String pagedResultCookie = null;
for (Map result : results) {
// special handling of paged result cookie
if (result.size() == 1) {
Map.Entry entry = result.entrySet().iterator().next();
if (OperationOptions.OP_PAGED_RESULTS_COOKIE.equalsIgnoreCase(entry.getKey())
&& entry.getValue() != null) {
pagedResultCookie = entry.getValue().toString();
}
} else {
ConnectorObjectBuilder cobld = new ConnectorObjectBuilder();
for (Map.Entry entry : result.entrySet()) {
final String attrName = entry.getKey();
final Object attrValue = entry.getValue();
// Special first
if (Uid.NAME.equalsIgnoreCase(attrName)) {
if (attrValue == null) {
throw new IllegalArgumentException("Uid cannot be null");
}
cobld.setUid(attrValue.toString());
} else if (Name.NAME.equalsIgnoreCase(attrName)) {
if (attrValue == null) {
throw new IllegalArgumentException("Name cannot be null");
}
cobld.setName(attrValue.toString());
} else if (attrName.equalsIgnoreCase("password")) {
// is there a chance we fetch password from search?
} else if (attrValue instanceof Collection) {
cobld.addAttribute(AttributeBuilder.build(attrName, (Collection>) attrValue));
} else if (attrValue != null) {
cobld.addAttribute(AttributeBuilder.build(attrName, attrValue));
} else {
cobld.addAttribute(AttributeBuilder.build(attrName));
}
}
cobld.setObjectClass(objectClass);
handler.handle(cobld.build());
LOG.ok("ConnectorObject is built");
}
}
if (handler instanceof SearchResultsHandler) {
SearchResultsHandler.class.cast(handler).handleResult(new SearchResult(pagedResultCookie, -1));
} else {
LOG.warn("Not expected, but found {0}: {1}",
OperationOptions.OP_PAGED_RESULTS_COOKIE, pagedResultCookie);
}
}
@SuppressWarnings("unchecked")
private void processDeltas(
final ObjectClass objectClass,
final List> results,
final SyncResultsHandler handler) {
for (Map result : results) {
// The Map should look like:
// token: token
// operation: CREATE_OR_UPDATE|DELETE (defaults to CREATE_OR_UPDATE)
// uid: uid
// previousUid: prevuid (This is for rename ops)
// password: password
// attributes: of attributes name/values
SyncDeltaBuilder syncbld = new SyncDeltaBuilder();
String uid = (String) result.get("uid");
if (uid != null && !uid.isEmpty()) {
syncbld.setUid(new Uid(uid));
Object token = result.get("token");
// Null token, set some acceptable value
if (token == null) {
LOG.ok("token value is null, replacing to 0L");
token = 0L;
}
syncbld.setToken(new SyncToken(token));
// Start building the connector object
ConnectorObjectBuilder cobld = new ConnectorObjectBuilder();
cobld.setName(uid);
cobld.setUid(uid);
cobld.setObjectClass(objectClass);
// operation
String op = (String) result.get("operation");
// if operation is null we assume this is CREATE_OR_UPDATE
syncbld.setDeltaType(op != null && SyncDeltaType.DELETE.name().equalsIgnoreCase(op)
? SyncDeltaType.DELETE
: SyncDeltaType.CREATE_OR_UPDATE);
// previous UID
String prevUid = (String) result.get("previousUid");
if (prevUid != null && !prevUid.isEmpty()) {
syncbld.setPreviousUid(new Uid(prevUid));
}
// password? is password valid if empty string? let's assume yes...
if (result.get("password") != null) {
cobld.addAttribute(
AttributeBuilder.buildCurrentPassword(((String) result.get("password")).toCharArray()));
}
// Remaining attributes
Object attributes = result.get("attributes");
if (attributes != null) {
for (Map.Entry attr : ((Map) attributes).entrySet()) {
final String attrName = attr.getKey();
final Object attrValue = attr.getValue();
if (attrValue instanceof Collection) {
cobld.addAttribute(AttributeBuilder.build(attrName, (Collection>) attrValue));
} else if (attrValue != null) {
cobld.addAttribute(AttributeBuilder.build(attrName, attrValue));
} else {
cobld.addAttribute(AttributeBuilder.build(attrName));
}
}
}
syncbld.setObject(cobld.build());
if (!handler.handle(syncbld.build())) {
LOG.ok("Stop processing of the sync result set");
break;
}
} else {
// we have a null uid... mmmm....
}
}
}
private boolean checkReloadScript(
final ScriptExecutor scriptExecutor,
final String scriptString,
final String scriptFileName) {
return (StringUtil.isNotBlank(scriptFileName) || StringUtil.isNotBlank(scriptString))
&& (scriptExecutor == null || config.isReloadScriptOnExecution());
}
}