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}