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-2023 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}