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 *      Copyright 2013-2018 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 * Data Broker instances in a deployment, the data store should be accessible
057 * 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 Broker
159   * shuts down. Any clean-up of this store adapter should be performed here.
160   * <p>
161   * The default implementation is empty.
162   */
163  public void finalizeStoreAdapter()
164  {
165    // No implementation is performed by default.
166  }
167
168  /**
169   * Retrieves a collection of attribute definitions describing the schema
170   * for objects supported by this Store Adapter. The default implementation
171   * returns an empty collection indicating that the schema is not defined.
172   * Use StoreAttributeDefinition.Builder to create instances of
173   * StoreAttributeDefinition.
174   *
175   * @return  The attribute definitions describing the schema, or an empty
176   *          collection if the schema is not defined.
177   */
178  public Collection<StoreAttributeDefinition> getNativeSchema()
179  {
180    return Collections.emptyList();
181  }
182
183  /**
184   * Determines whether this Store Adapter is currently available and
185   * in-service. This may return {@code false} in the case that all
186   * backend servers are down, for example; during this time the
187   * SCIMResourceType will return an appropriate 503 response code to clients.
188   *
189   * @return {@code true} if the Store Adapter initialized and connected;
190   *         {@code false} otherwise.
191   */
192  public abstract boolean isAvailable();
193
194  /**
195   * Fetches the specified entry.
196   *
197   * @param request  The retrieve request.
198   * @return  The retrieved entry as a JSON object. This string can be created
199   *          by any JSON library. For example, the Jackson library's
200   *          ObjectNode.toString() or the UnboundID LDAP SDK's
201   *          JSONObject.toString().
202   * @throws ScimException  If there is a problem fulfilling the request.
203   */
204  public abstract String retrieve(
205          final StoreRetrieveRequest request) throws ScimException;
206
207  /**
208   * Search for entries in the native store which could match the
209   * specified criteria. The contract is for the store adapter to return a
210   * superset of matching entries, i.e. The store adapter must return all
211   * entries which match the specified filter but may also return entries
212   * which do not match. The results from the store adapter are subsequently
213   * filtered by the Broker.
214   *
215   * @param request  The search request.
216   * @throws ScimException  If there is a problem fulfilling the search request.
217   */
218  public abstract void search(
219          final StoreSearchRequest request) throws ScimException;
220
221  /**
222   * Create the specified entry in the native data store.
223   *
224   * @param request  The create request.
225   * @return The entry that was just created as a JSON object. This string can
226   *         be created by any JSON library. For example, the Jackson library's
227   *         ObjectNode.toString() or the UnboundID LDAP SDK's
228   *         JSONObject.toString().
229   * @throws ScimException  If there is a problem creating the entry.
230   */
231  public abstract String create(final StoreCreateRequest request)
232          throws ScimException;
233
234  /**
235   * Update the specified entry in the native data store.
236   *
237   * @param request  The update request.
238   * @return  The updated resource as a JSON object. This string can be created
239   *          by any JSON library. For example, the Jackson library's
240   *          ObjectNode.toString() or the UnboundID LDAP SDK's
241   *          JSONObject.toString().
242   * @throws ScimException  If there is a problem modifying the entry.
243   */
244  public abstract String update(final StoreUpdateRequest request)
245          throws ScimException;
246
247  /**
248   * Delete the specified entry from the native data store.
249   *
250   * @param request  The delete request.
251   * @throws ScimException  If there is a problem deleting the entry.
252   */
253  public abstract void delete(final StoreDeleteRequest request)
254          throws ScimException;
255}