001 /*
002 * Copyright 2011-2012 UnboundID Corp.
003 *
004 * This program is free software; you can redistribute it and/or modify
005 * it under the terms of the GNU General Public License (GPLv2 only)
006 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
007 * as published by the Free Software Foundation.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012 * GNU General Public License for more details.
013 *
014 * You should have received a copy of the GNU General Public License
015 * along with this program; if not, see <http://www.gnu.org/licenses>.
016 */
017
018 package com.unboundid.scim.sdk;
019
020 import com.unboundid.scim.data.GroupResource;
021 import com.unboundid.scim.data.BaseResource;
022 import com.unboundid.scim.data.ResourceFactory;
023 import com.unboundid.scim.data.ServiceProviderConfig;
024 import com.unboundid.scim.data.UserResource;
025 import com.unboundid.scim.schema.CoreSchema;
026 import com.unboundid.scim.schema.ResourceDescriptor;
027 import org.apache.wink.client.ClientConfig;
028 import org.apache.wink.client.RestClient;
029
030 import javax.ws.rs.core.MediaType;
031 import java.net.URI;
032 import java.util.List;
033
034
035
036 /**
037 * The SCIMService class represents a client connection to a SCIM service
038 * provider. It handles setting up and configuring the connection which will
039 * be used by the SCIMEndpoints that are obtained form this SCIMService.
040 */
041 public class SCIMService
042 {
043 private final RestClient client;
044 private final URI baseURL;
045
046 private MediaType acceptType = MediaType.APPLICATION_JSON_TYPE;
047 private MediaType contentType = MediaType.APPLICATION_JSON_TYPE;
048 private final boolean[] overrides = new boolean[3];
049 private String userAgent;
050
051 /**
052 * Constructs a new SCIMService that is configured from the provided
053 * <code>org.apache.wink.client.ClientConfig</code> instance.
054 *
055 * @param baseUrl The SCIM Service Provider URL.
056 * @param clientConfig The configuration to use.
057 */
058 public SCIMService(final URI baseUrl, final ClientConfig clientConfig)
059 {
060 this.baseURL = baseUrl;
061 this.client = new RestClient(clientConfig);
062 }
063
064 /**
065 * Constructs a new SCIMService that uses
066 * <code>java.net.HttpURLConnection</code> for the HTTP layer.
067 *
068 * @param baseUrl The SCIM Service Provider URL.
069 */
070 public SCIMService(final URI baseUrl)
071 {
072 this(baseUrl, new ClientConfig());
073 }
074
075 /**
076 * Constructs a new SCIMService with basic authentication support
077 * using the provided credentials. This SCIMService will use
078 * <code>java.net.HttpURLConnection</code> for the HTTP layer.
079 *
080 * @param baseUrl The SCIM Service Provider URL.
081 * @param username The username.
082 * @param password The password.
083 */
084 public SCIMService(final URI baseUrl, final String username,
085 final String password)
086 {
087 this(baseUrl, new ClientConfig().handlers(new HttpBasicAuthSecurityHandler
088 (username,password)));
089 }
090
091 /**
092 * Returns a SCIMEndpoint with the current settings that can be used to
093 * invoke CRUD operations. Any changes to the SCIMService configuration will
094 * not be reflected in the returned SCIMEndpoint.
095 *
096 * @param resourceDescriptor The ResourceDescriptor of the endpoint.
097 * @param resourceFactory The ResourceFactory that should be used to
098 * create SCIM resource instances.
099 * @param <R> The type of SCIM resource instances.
100 * @return The SCIMEndpoint that can be used to invoke CRUD operations.
101 */
102 public <R extends BaseResource> SCIMEndpoint<R> getEndpoint(
103 final ResourceDescriptor resourceDescriptor,
104 final ResourceFactory<R> resourceFactory)
105 {
106 return new SCIMEndpoint<R>(this, client, resourceDescriptor,
107 resourceFactory);
108 }
109
110 /**
111 * Returns a SCIMEndpoint for the Users endpoint defined in the core schema.
112 *
113 * @return The SCIMEndpoint for the Users endpoint defined in the core schema.
114 */
115 public SCIMEndpoint<UserResource> getUserEndpoint()
116 {
117 return new SCIMEndpoint<UserResource>(this, client,
118 CoreSchema.USER_DESCRIPTOR, UserResource.USER_RESOURCE_FACTORY);
119 }
120
121 /**
122 * Returns a SCIMEndpoint for the Groups endpoint defined in the core schema.
123 *
124 * @return The SCIMEndpoint for the Groups endpoint defined in the
125 * core schema.
126 */
127 public SCIMEndpoint<GroupResource> getGroupEndpoint()
128 {
129 return new SCIMEndpoint<GroupResource>(this, client,
130 CoreSchema.GROUP_DESCRIPTOR, GroupResource.GROUP_RESOURCE_FACTORY);
131 }
132
133 /**
134 * Returns a SCIMEndpoint for the Schemas endpoint. This endpoint allows for
135 * the retrieval of schema for all service provider supported resources.
136 *
137 * @return The SCIMEndpoint for the Schemas endpoint.
138 */
139 public SCIMEndpoint<ResourceDescriptor> getResourceSchemaEndpoint()
140 {
141 return new SCIMEndpoint<ResourceDescriptor>(this, client,
142 CoreSchema.RESOURCE_SCHEMA_DESCRIPTOR,
143 ResourceDescriptor.RESOURCE_DESCRIPTOR_FACTORY);
144 }
145
146 /**
147 * Retrieves the ResourceDescriptor for the specified resource from the
148 * SCIM service provider.
149 *
150 * @param resourceName The name of the resource.
151 * @param schema The schema URN of the resource or <code>null</code>
152 * to match only based on the name of the resource.
153 * @return The ResourceDescriptor for the specified resource or
154 * <code>null</code> if none are found.
155 * @throws SCIMException If the ResourceDescriptor could not be read.
156 */
157 public ResourceDescriptor getResourceDescriptor(final String resourceName,
158 final String schema)
159 throws SCIMException
160 {
161 final SCIMEndpoint<ResourceDescriptor> endpoint =
162 getResourceSchemaEndpoint();
163 String filter = "name eq \"" + resourceName + "\"";
164 if(schema != null)
165 {
166 filter += " and schema eq \"" + schema + "\"";
167 }
168 final Resources<ResourceDescriptor> resources = endpoint.query(filter);
169 if(resources.getTotalResults() == 0)
170 {
171 return null;
172 }
173 if(resources.getTotalResults() > 1)
174 {
175 throw new InvalidResourceException(
176 "The service provider returned multiple resource descriptors " +
177 "with resource name '" + resourceName);
178 }
179 return resources.iterator().next();
180 }
181
182
183
184 /**
185 * Retrieves the Service Provider Config from the SCIM service provider.
186 *
187 * @return The Service Provider Config.
188 *
189 * @throws SCIMException If the Service Provider Config could not be read.
190 */
191 public ServiceProviderConfig getServiceProviderConfig()
192 throws SCIMException
193 {
194 final SCIMEndpoint<ServiceProviderConfig> endpoint =
195 getEndpoint(CoreSchema.SERVICE_PROVIDER_CONFIG_SCHEMA_DESCRIPTOR,
196 ServiceProviderConfig.SERVICE_PROVIDER_CONFIG_RESOURCE_FACTORY);
197
198 // The ServiceProviderConfig is a special case where there is only a
199 // single resource at the endpoint, so the id is not specified.
200 return endpoint.get(null);
201 }
202
203
204
205 /**
206 * Invoke a bulk request. The service provider will perform as
207 * many operations as possible without regard to the number of failures.
208 *
209 * @param operations The operations to be performed.
210 *
211 * @return The bulk response.
212 *
213 * @throws SCIMException If the request fails.
214 */
215 public BulkResponse processBulkRequest(
216 final List<BulkOperation> operations)
217 throws SCIMException
218 {
219 return processBulkRequest(operations, -1);
220 }
221
222
223
224 /**
225 * Invoke a bulk request.
226 *
227 * @param operations The operations to be performed.
228 * @param failOnErrors The number of errors that the service provider will
229 * accept before the operation is terminated and an
230 * error response is returned. A value of -1 indicates
231 * the the service provider will continue to perform
232 * as many operations as possible without regard to
233 * failures.
234 *
235 * @return The bulk response.
236 *
237 * @throws SCIMException If the request fails.
238 */
239 public BulkResponse processBulkRequest(
240 final List<BulkOperation> operations,
241 final int failOnErrors)
242 throws SCIMException
243 {
244 final BulkEndpoint request = new BulkEndpoint(this, client);
245
246 return request.processRequest(operations, failOnErrors);
247 }
248
249
250
251 /**
252 * Retrieves the SCIM Service Provider URL.
253 *
254 * @return The SCIM Service Provider URL.
255 */
256 public URI getBaseURL() {
257 return baseURL;
258 }
259
260 /**
261 * Retrieves the content media type that should be used when writing data to
262 * the SCIM service provider.
263 *
264 * @return The content media type that should be used when writing data to
265 * the SCIM service provider.
266 */
267 public MediaType getContentType() {
268 return contentType;
269 }
270
271 /**
272 * Sets the content media type that should be used when writing data to
273 * the SCIM service provider.
274 *
275 * @param contentType he content media type that should be used when writing
276 * data to the SCIM service provider.
277 */
278 public void setContentType(final MediaType contentType) {
279 this.contentType = contentType;
280 }
281
282 /**
283 * Retrieves the accept media type that should be used when reading data from
284 * the SCIM service provider.
285 *
286 * @return The accept media type that should be used when reading data from
287 * the SCIM service provider.
288 */
289 public MediaType getAcceptType() {
290 return acceptType;
291 }
292
293 /**
294 * Sets the accept media type that should be used when reading data from
295 * the SCIM service provider.
296 *
297 * @param acceptType The accept media type that should be used when reading
298 * data from the SCIM service provider.
299 */
300 public void setAcceptType(final MediaType acceptType) {
301 this.acceptType = acceptType;
302 }
303
304 /**
305 * Retrieves the user-agent string that will be used in the HTTP request
306 * headers.
307 *
308 * @return The user-agent string. This may be null, in which case a default
309 * user-agent will be used.
310 */
311 public String getUserAgent() {
312 return userAgent;
313 }
314
315 /**
316 * Sets the user-agent string to use in the request headers.
317 *
318 * @param userAgent The user-agent string that should be used.
319 */
320 public void setUserAgent(final String userAgent) {
321 this.userAgent = userAgent;
322 }
323
324 /**
325 * Whether to override DELETE operations with POST.
326 *
327 * @return <code>true</code> to override DELETE operations with POST or
328 * <code>false</code> to use the DELETE method.
329 */
330 public boolean isOverrideDelete() {
331 return overrides[2];
332 }
333
334 /**
335 * Sets whether to override DELETE operations with POST.
336 *
337 * @param overrideDelete <code>true</code> to override DELETE operations with
338 * POST or <code>false</code> to use the DELETE method.
339 */
340 public void setOverrideDelete(final boolean overrideDelete) {
341 this.overrides[2] = overrideDelete;
342 }
343
344 /**
345 * Whether to override PATCH operations with POST.
346 *
347 * @return <code>true</code> to override PATCH operations with POST or
348 * <code>false</code> to use the PATCH method.
349 */
350 public boolean isOverridePatch() {
351 return overrides[1];
352 }
353
354 /**
355 * Sets whether to override PATCH operations with POST.
356 *
357 * @param overridePatch <code>true</code> to override PATCH operations with
358 * POST or <code>false</code> to use the PATCH method.
359 */
360 public void setOverridePatch(final boolean overridePatch) {
361 this.overrides[1] = overridePatch;
362 }
363
364 /**
365 * Whether to override PUT operations with POST.
366 *
367 * @return <code>true</code> to override PUT operations with POST or
368 * <code>false</code> to use the PUT method.
369 */
370 public boolean isOverridePut() {
371 return overrides[0];
372 }
373
374 /**
375 * Sets whether to override PUT operations with POST.
376 *
377 * @param overridePut <code>true</code> to override PUT operations with
378 * POST or <code>false</code> to use the PUT method.
379 */
380 public void setOverridePut(final boolean overridePut) {
381 this.overrides[0] = overridePut;
382 }
383 }