001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * http://www.opensource.org/licenses/cddl1.php.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file. If applicable, add the following below this CDDL HEADER,
016 * with the fields enclosed by brackets "[]" replaced with your own
017 * identifying information:
018 *      Portions Copyright [yyyy] [name of copyright owner]
019 *
020 * CDDL HEADER END
021 *
022 *
023 *      Portions Copyright 2013-2024 Ping Identity Corporation
024 */
025package com.unboundid.directory.sdk.broker.api;
026
027import com.unboundid.directory.sdk.broker.config.StoreAdapterConfig;
028import com.unboundid.directory.sdk.broker.internal.BrokerExtension;
029import com.unboundid.directory.sdk.broker.types.BrokerContext;
030import com.unboundid.directory.sdk.broker.types.StoreAttributeDefinition;
031import com.unboundid.directory.sdk.broker.types.StoreCreateRequest;
032import com.unboundid.directory.sdk.broker.types.StoreDeleteRequest;
033import com.unboundid.directory.sdk.broker.types.StoreSearchRequest;
034import com.unboundid.directory.sdk.broker.types.StoreRetrieveRequest;
035import com.unboundid.directory.sdk.broker.types.StoreUpdateRequest;
036import com.unboundid.directory.sdk.common.internal.Configurable;
037import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
038import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
039import com.unboundid.scim2.common.exceptions.ScimException;
040import com.unboundid.util.Extensible;
041import com.unboundid.util.ThreadSafety;
042import com.unboundid.util.ThreadSafetyLevel;
043import com.unboundid.util.args.ArgumentException;
044import com.unboundid.util.args.ArgumentParser;
045
046import java.util.Collection;
047import java.util.Collections;
048import java.util.List;
049import java.util.Map;
050
051
052/**
053 * This class defines an API that must be implemented by extensions that need
054 * to store user data in some non-LDAP data store. This adapter API is generic
055 * and can support a wide range of repositories. When using multiple
056 * PingAuthorize Server instances in a deployment, the data store should be
057 * accessible from all instances.
058 * <p>
059 * After the Store Adapter is initialized (via the initializeStoreAdapter()
060 * method), all methods will be guarded by a call to {@link #isAvailable()} to
061 * make sure that the adapter is currently connected and ready to service
062 * requests. If this returns {@code false}, the SCIMResourceType will return an
063 * appropriate 503 response code to clients until the adapter becomes available
064 * again.
065 * <H2>Configuring Store Adapters</H2>
066 * In order to configure a store adapter created using this API, use
067 * a command like:
068 * <PRE>
069 *      dsconfig create-store-adapter \
070 *           ---adapter-name "<I>{name}</I>" \
071 *           --type third-party \
072 *           --set "extension-class:<I>{class-name}</I>" \
073 *           --set "extension-argument:<I>{name=value}</I>"
074 * </PRE>
075 * where "<I>{name}</I>" is the name to use for the store adapter
076 * instance, "<I>{class-name}</I>" is the fully-qualified name of the Java class
077 * that extends
078 * {@code com.unboundid.directory.sdk.broker.api.StoreAdapter},
079 * and "<I>{name=value}</I>" represents name-value pairs for any arguments to
080 * provide to the store adapter. If multiple arguments should be
081 * provided to the extension, then the
082 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be
083 * provided multiple times.
084 */
085@Extensible()
086@BrokerExtension
087@ThreadSafety(level= ThreadSafetyLevel.INTERFACE_THREADSAFE)
088public abstract class StoreAdapter implements UnboundIDExtension,
089        Configurable, ExampleUsageProvider
090{
091  /**
092   * Creates a new instance of this store adapter.  All
093   * implementations must include a default constructor, but any
094   * initialization should generally be done in the
095   * {@link #initializeStoreAdapter} method.
096   */
097  public StoreAdapter()
098  {
099    // No implementation is required.
100  }
101
102  /**
103   * {@inheritDoc}
104   */
105  @Override
106  public abstract String getExtensionName();
107
108  /**
109   * {@inheritDoc}
110   */
111  @Override
112  public abstract String[] getExtensionDescription();
113
114  /**
115   * {@inheritDoc}
116   */
117  @Override
118  public Map<List<String>,String> getExamplesArgumentSets()
119  {
120    return Collections.emptyMap();
121  }
122
123  /**
124   * {@inheritDoc}
125   */
126  @Override
127  public void defineConfigArguments(final ArgumentParser parser)
128         throws ArgumentException
129  {
130    // No arguments will be allowed by default.
131  }
132
133  /**
134   * Initializes this store adapter. Any initialization should be performed
135   * here. This method should generally store the {@link BrokerContext} in
136   * a class member so that it can be used elsewhere in the implementation.
137   * <p>
138   * The default implementation is empty.
139   *
140   * @param  serverContext  A handle to the server context for the server in
141   *                        which this extension is running. Extensions should
142   *                        typically store this in a class member.
143   * @param  config         The general configuration for this object.
144   * @param  parser         The argument parser which has been initialized from
145   *                        the configuration for this store adapter.
146   * @throws Exception      If a problem occurs while initializing this store
147   *                        adapter.
148   */
149  public void initializeStoreAdapter(final BrokerContext serverContext,
150                                     final StoreAdapterConfig config,
151                                     final ArgumentParser parser)
152      throws Exception
153  {
154    // No initialization will be performed by default.
155  }
156
157  /**
158   * This hook is called when the SCIMResourceType is disabled or the
159   * PingAuthorize Server shuts down. Any clean-up of this store adapter should
160   * be performed here.
161   * <p>
162   * The default implementation is empty.
163   */
164  public void finalizeStoreAdapter()
165  {
166    // No implementation is performed by default.
167  }
168
169  /**
170   * Retrieves a collection of attribute definitions describing the schema
171   * for objects supported by this Store Adapter. The default implementation
172   * returns an empty collection indicating that the schema is not defined.
173   * Use StoreAttributeDefinition.Builder to create instances of
174   * StoreAttributeDefinition.
175   *
176   * @return  The attribute definitions describing the schema, or an empty
177   *          collection if the schema is not defined.
178   */
179  public Collection<StoreAttributeDefinition> getNativeSchema()
180  {
181    return Collections.emptyList();
182  }
183
184  /**
185   * Determines whether this Store Adapter is currently available and
186   * in-service. This may return {@code false} in the case that all
187   * backend servers are down, for example; during this time the
188   * SCIMResourceType will return an appropriate 503 response code to clients.
189   *
190   * @return {@code true} if the Store Adapter initialized and connected;
191   *         {@code false} otherwise.
192   */
193  public abstract boolean isAvailable();
194
195  /**
196   * Fetches the specified entry.
197   *
198   * @param request  The retrieve request.
199   * @return  The retrieved entry as a JSON object. This string can be created
200   *          by any JSON library. For example, the Jackson library's
201   *          ObjectNode.toString() or the UnboundID LDAP SDK's
202   *          JSONObject.toString().
203   * @throws ScimException  If there is a problem fulfilling the request.
204   */
205  public abstract String retrieve(
206          final StoreRetrieveRequest request) throws ScimException;
207
208  /**
209   * Search for entries in the native store which could match the
210   * specified criteria. The contract is for the store adapter to return a
211   * superset of matching entries, i.e. The store adapter must return all
212   * entries which match the specified filter but may also return entries
213   * which do not match. The results from the store adapter are subsequently
214   * filtered by the PingAuthorize Server.
215   *
216   * @param request  The search request.
217   * @throws ScimException  If there is a problem fulfilling the search request.
218   */
219  public abstract void search(
220          final StoreSearchRequest request) throws ScimException;
221
222  /**
223   * Create the specified entry in the native data store.
224   *
225   * @param request  The create request.
226   * @return The entry that was just created as a JSON object. This string can
227   *         be created by any JSON library. For example, the Jackson library's
228   *         ObjectNode.toString() or the UnboundID LDAP SDK's
229   *         JSONObject.toString().
230   * @throws ScimException  If there is a problem creating the entry.
231   */
232  public abstract String create(final StoreCreateRequest request)
233          throws ScimException;
234
235  /**
236   * Update the specified entry in the native data store.
237   *
238   * @param request  The update request.
239   * @return  The updated resource as a JSON object. This string can be created
240   *          by any JSON library. For example, the Jackson library's
241   *          ObjectNode.toString() or the UnboundID LDAP SDK's
242   *          JSONObject.toString().
243   * @throws ScimException  If there is a problem modifying the entry.
244   */
245  public abstract String update(final StoreUpdateRequest request)
246          throws ScimException;
247
248  /**
249   * Delete the specified entry from the native data store.
250   *
251   * @param request  The delete request.
252   * @throws ScimException  If there is a problem deleting the entry.
253   */
254  public abstract void delete(final StoreDeleteRequest request)
255          throws ScimException;
256}