/* * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2008-2009 Sun Microsystems, Inc. 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]" * ==================== * Portions Copyrighted 2010-2013 ForgeRock AS. */ package org.identityconnectors.framework.common.objects; import java.util.HashMap; import java.util.Map; import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.PrettyStringBuilder; import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.framework.api.operations.ScriptOnResourceApiOp; import org.identityconnectors.framework.api.operations.SearchApiOp; import org.identityconnectors.framework.api.operations.SyncApiOp; import org.identityconnectors.framework.common.FrameworkUtil; import org.identityconnectors.framework.common.serializer.ObjectSerializerFactory; import org.identityconnectors.framework.common.serializer.SerializerUtil; /** * Arbitrary options to be passed into various operations. This serves as a * catch-all for extra options. */ public final class OperationOptions { /** * An option to use with {@link SearchApiOp} (in conjunction with * {@link #OP_CONTAINER}) that specifies how far beneath the * {@linkplain #OP_CONTAINER specified container} to search. Must be one of * the following values: * <ol> * <li>{@link #SCOPE_OBJECT}</li> * <li>{@link #SCOPE_ONE_LEVEL}</li> * <li>{@link #SCOPE_SUBTREE}</li> * </ol> */ public static final String OP_SCOPE = "SCOPE"; /** * A value of {@link #OP_SCOPE} that indicates to search for the * {@linkplain #OP_CONTAINER specified container} <em>object itself</em>. */ public static final String SCOPE_OBJECT = "object"; /** * A value of {@link #OP_SCOPE} that indicates to search for objects that * the {@linkplain #OP_CONTAINER specified container} * <em>directly contains</em>. */ public static final String SCOPE_ONE_LEVEL = "onelevel"; /** * A value of {@link #OP_SCOPE} that indicates to search for objects that * the {@linkplain #OP_CONTAINER specified container} * <em>directly or indirectly contains</em>. */ public static final String SCOPE_SUBTREE = "subtree"; /** * An option to use with {@link SearchApiOp} that specifies the container * under which to perform the search. Must be of type {@link QualifiedUid}. * Should be implemented for those object classes whose * {@link ObjectClassInfo#isContainer()} returns true. */ public static final String OP_CONTAINER = "CONTAINER"; /** * An option to use with {@link ScriptOnResourceApiOp} and possibly others * that specifies an account under which to execute the script/operation. * The specified account will appear to have performed any action that the * script/operation performs. * <p> * Check the javadoc for a particular connector to see whether that * connector supports this option. */ public static final String OP_RUN_AS_USER = "RUN_AS_USER"; /** * An option to use with {@link ScriptOnResourceApiOp} and possibly others * that specifies a password under which to execute the script/operation. */ public static final String OP_RUN_WITH_PASSWORD = "RUN_WITH_PASSWORD"; /** * Determines which attributes to retrieve during {@link SearchApiOp} and * {@link SyncApiOp}. * <p> * This option overrides the default behavior, which is for the connector to * return exactly the set of attributes that are identified as * {@link AttributeInfo#isReturnedByDefault() returned by default} in the * schema for that connector. * <p> * This option allows a client application to request <i>additional * attributes</i> that would not otherwise not be returned (generally * because such attributes are more expensive for a connector to fetch and * to format) and/or to request only a <i>subset of the attributes</i> that * would normally be returned. */ public static final String OP_ATTRIBUTES_TO_GET = "ATTRS_TO_GET"; /** * An option to use with {@link SearchApiOp} that specifies an opaque cookie * which is used by the connector to track its position in the set of query * results. * @since 1.4 */ public static final String OP_PAGED_RESULTS_COOKIE = "PAGED_RESULTS_COOKIE"; /** * An option to use with {@link SearchApiOp} that specifies the index within * the result set of the first result which should be returned. * @since 1.4 */ public static final String OP_PAGED_RESULTS_OFFSET = "PAGED_RESULTS_OFFSET"; /** * An option to use with {@link SearchApiOp} that specifies the requested * page results page size. * @since 1.4 */ public static final String OP_PAGE_SIZE = "PAGE_SIZE"; /** * An option to use with {@link SearchApiOp} that specifies the sort keys * which should be used for ordering the {@link ConnectorObject} returned by * search request. * @since 1.4 */ public static final String OP_SORT_KEYS = "SORT_KEYS"; private final Map<String, Object> operationOptions; /** * Public only for serialization; please use {@link OperationOptionsBuilder} * . * * @param operationOptions * The options. */ public OperationOptions(Map<String, Object> operationOptions) { for (Object value : operationOptions.values()) { FrameworkUtil.checkOperationOptionValue(value); } // clone options to do a deep copy in case anything // is an array @SuppressWarnings("unchecked") Map<String, Object> operationOptionsClone = (Map<String, Object>) SerializerUtil.cloneObject(operationOptions); this.operationOptions = CollectionUtil.asReadOnlyMap(operationOptionsClone); } // NOTE: this method makes a heavy assumption that in OpenICF only arrays // occur as mutable values in operation options, and that there is a single // level of array/nesting. // Really would be better if OpenICF switched to List and not array // To more easily return immutable views private Map<String, Object> copyMutables(Map<String, Object> operationOptions) { Map<String, Object> operationOptionsCopy = new HashMap<String, Object>(operationOptions); for (Map.Entry<String, Object> entry : operationOptionsCopy.entrySet()) { if (entry.getValue() instanceof Object[]) { entry.setValue(((Object[]) entry.getValue()).clone()); } } return operationOptionsCopy; } /** * Returns a map of options. Each value in the map must be of a type that * the framework can serialize. See {@link ObjectSerializerFactory} for a * list of supported types. * * @return A map of options. */ public Map<String, Object> getOptions() { return operationOptions; } /** * Add basic debugging of internal data. {@inheritDoc} */ @Override public String toString() { StringBuilder bld = new StringBuilder(); bld.append("OperationOptions: ").append(new PrettyStringBuilder().toString(getOptions())); return bld.toString(); } /** * Convenience method that returns {@link #OP_SCOPE}. * * @return The value for {@link #OP_SCOPE}. */ public String getScope() { return (String) operationOptions.get(OP_SCOPE); } /** * Convenience method that returns {@link #OP_CONTAINER}. * * @return The value for {@link #OP_CONTAINER}. */ public QualifiedUid getContainer() { return (QualifiedUid) operationOptions.get(OP_CONTAINER); } /** * Get the string array of attribute names to return in the object. */ public String[] getAttributesToGet() { return (String[]) operationOptions.get(OP_ATTRIBUTES_TO_GET); } /** * Get the account to run the operation as.. */ public String getRunAsUser() { return (String) operationOptions.get(OP_RUN_AS_USER); } /** * Get the password to run the operation as.. */ public GuardedString getRunWithPassword() { return (GuardedString) operationOptions.get(OP_RUN_WITH_PASSWORD); } /** * Returns the opaque cookie which is used by the Connector to track its * position in the set of query results. Paged results will be enabled if * and only if the page size is non-zero. * <p> * The cookie must be {@code null} in the initial search request sent by the * client. For subsequent search requests the client must include the cookie * returned with the previous search result, until the resource provider * returns a {@code null} cookie indicating that the final page of results * has been returned. * * @return The opaque cookie which is used by the Connector to track its * position in the set of search results, or {@code null} if paged * results are not requested (when the page size is 0), or if the * first page of results is being requested (when the page size is * non-zero). * @see #getPageSize() * @see #getPagedResultsOffset() * @since 1.4 */ public String getPagedResultsCookie() { return (String) operationOptions.get(OP_PAGED_RESULTS_COOKIE); }; /** * Returns the index within the result set of the first result which should * be returned. Paged results will be enabled if and only if the page size * is non-zero. If the parameter is not present or a value less than 1 is * specified then the page following the previous page returned will be * returned. A value equal to or greater than 1 indicates that a specific * page should be returned starting from the position specified. * * @return The index within the result set of the first result which should * be returned. * @see #getPageSize() * @see #getPagedResultsCookie() * @since 1.4 */ public Integer getPagedResultsOffset() { return (Integer) operationOptions.get(OP_PAGED_RESULTS_OFFSET); }; /** * Returns the requested page results page size or {@code 0} if paged * results are not required. For all paged result requests other than the * initial request, a cookie should be provided with the search request. See * {@link #getPagedResultsCookie()} for more information. * * @return The requested page results page size or {@code 0} if paged * results are not required. * @see #getPagedResultsCookie() * @see #getPagedResultsOffset() * @since 1.4 */ public Integer getPageSize() { return (Integer) operationOptions.get(OP_PAGE_SIZE); }; /** * Returns the sort keys which should be used for ordering the * {@link ConnectorObject}s returned by this search request. * * @return The sort keys which should be used for ordering the * {@link ConnectorObject}s returned by this search request (never * {@code null}). * @since 1.4 */ @SuppressWarnings("unchecked") public SortKey[] getSortKeys() { return (SortKey[]) operationOptions.get(OP_SORT_KEYS); }; }