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 OAuth 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 oAuthToken The OAuth token.
082 */
083 public SCIMService(final URI baseUrl, final OAuthToken oAuthToken) {
084 this(baseUrl, new ClientConfig().handlers(new OAuthSecurityHandler
085 (oAuthToken)));
086 }
087
088 /**
089 * Constructs a new SCIMService with basic authentication support
090 * using the provided credentials. This SCIMService will use
091 * <code>java.net.HttpURLConnection</code> for the HTTP layer.
092 *
093 * @param baseUrl The SCIM Service Provider URL.
094 * @param username The username.
095 * @param password The password.
096 */
097 public SCIMService(final URI baseUrl, final String username,
098 final String password)
099 {
100 this(baseUrl, new ClientConfig().handlers(new HttpBasicAuthSecurityHandler
101 (username,password)));
102 }
103
104 /**
105 * Returns a SCIMEndpoint with the current settings that can be used to
106 * invoke CRUD operations. Any changes to the SCIMService configuration will
107 * not be reflected in the returned SCIMEndpoint.
108 *
109 * @param resourceDescriptor The ResourceDescriptor of the endpoint.
110 * @param resourceFactory The ResourceFactory that should be used to
111 * create SCIM resource instances.
112 * @param <R> The type of SCIM resource instances.
113 * @return The SCIMEndpoint that can be used to invoke CRUD operations.
114 */
115 public <R extends BaseResource> SCIMEndpoint<R> getEndpoint(
116 final ResourceDescriptor resourceDescriptor,
117 final ResourceFactory<R> resourceFactory)
118 {
119 return new SCIMEndpoint<R>(this, client, resourceDescriptor,
120 resourceFactory);
121 }
122
123 /**
124 * Returns a SCIMEndpoint for the Users endpoint defined in the core schema.
125 *
126 * @return The SCIMEndpoint for the Users endpoint defined in the core schema.
127 */
128 public SCIMEndpoint<UserResource> getUserEndpoint()
129 {
130 return new SCIMEndpoint<UserResource>(this, client,
131 CoreSchema.USER_DESCRIPTOR, UserResource.USER_RESOURCE_FACTORY);
132 }
133
134 /**
135 * Returns a SCIMEndpoint for the Groups endpoint defined in the core schema.
136 *
137 * @return The SCIMEndpoint for the Groups endpoint defined in the
138 * core schema.
139 */
140 public SCIMEndpoint<GroupResource> getGroupEndpoint()
141 {
142 return new SCIMEndpoint<GroupResource>(this, client,
143 CoreSchema.GROUP_DESCRIPTOR, GroupResource.GROUP_RESOURCE_FACTORY);
144 }
145
146 /**
147 * Returns a SCIMEndpoint for the Schemas endpoint. This endpoint allows for
148 * the retrieval of schema for all service provider supported resources.
149 *
150 * @return The SCIMEndpoint for the Schemas endpoint.
151 */
152 public SCIMEndpoint<ResourceDescriptor> getResourceSchemaEndpoint()
153 {
154 return new SCIMEndpoint<ResourceDescriptor>(this, client,
155 CoreSchema.RESOURCE_SCHEMA_DESCRIPTOR,
156 ResourceDescriptor.RESOURCE_DESCRIPTOR_FACTORY);
157 }
158
159 /**
160 * Retrieves the ResourceDescriptor for the specified resource from the
161 * SCIM service provider.
162 *
163 * @param resourceName The name of the resource.
164 * @param schema The schema URN of the resource or <code>null</code>
165 * to match only based on the name of the resource.
166 * @return The ResourceDescriptor for the specified resource or
167 * <code>null</code> if none are found.
168 * @throws SCIMException If the ResourceDescriptor could not be read.
169 */
170 public ResourceDescriptor getResourceDescriptor(final String resourceName,
171 final String schema)
172 throws SCIMException
173 {
174 final SCIMEndpoint<ResourceDescriptor> endpoint =
175 getResourceSchemaEndpoint();
176 String filter = "name eq \"" + resourceName + "\"";
177 if(schema != null)
178 {
179 filter += " and schema eq \"" + schema + "\"";
180 }
181 final Resources<ResourceDescriptor> resources = endpoint.query(filter);
182 if(resources.getTotalResults() == 0)
183 {
184 return null;
185 }
186 if(resources.getTotalResults() > 1)
187 {
188 throw new InvalidResourceException(
189 "The service provider returned multiple resource descriptors " +
190 "with resource name '" + resourceName);
191 }
192 return resources.iterator().next();
193 }
194
195
196
197 /**
198 * Retrieves the Service Provider Config from the SCIM service provider.
199 *
200 * @return The Service Provider Config.
201 *
202 * @throws SCIMException If the Service Provider Config could not be read.
203 */
204 public ServiceProviderConfig getServiceProviderConfig()
205 throws SCIMException
206 {
207 final SCIMEndpoint<ServiceProviderConfig> endpoint =
208 getEndpoint(CoreSchema.SERVICE_PROVIDER_CONFIG_SCHEMA_DESCRIPTOR,
209 ServiceProviderConfig.SERVICE_PROVIDER_CONFIG_RESOURCE_FACTORY);
210
211 // The ServiceProviderConfig is a special case where there is only a
212 // single resource at the endpoint, so the id is not specified.
213 return endpoint.get(null);
214 }
215
216
217
218 /**
219 * Invoke a bulk request. The service provider will perform as
220 * many operations as possible without regard to the number of failures.
221 *
222 * @param operations The operations to be performed.
223 *
224 * @return The bulk response.
225 *
226 * @throws SCIMException If the request fails.
227 */
228 public BulkResponse processBulkRequest(
229 final List<BulkOperation> operations)
230 throws SCIMException
231 {
232 return processBulkRequest(operations, -1);
233 }
234
235
236
237 /**
238 * Invoke a bulk request.
239 *
240 * @param operations The operations to be performed.
241 * @param failOnErrors The number of errors that the service provider will
242 * accept before the operation is terminated and an
243 * error response is returned. A value of -1 indicates
244 * the the service provider will continue to perform
245 * as many operations as possible without regard to
246 * failures.
247 *
248 * @return The bulk response.
249 *
250 * @throws SCIMException If the request fails.
251 */
252 public BulkResponse processBulkRequest(
253 final List<BulkOperation> operations,
254 final int failOnErrors)
255 throws SCIMException
256 {
257 final BulkEndpoint request = new BulkEndpoint(this, client);
258
259 return request.processRequest(operations, failOnErrors);
260 }
261
262
263
264 /**
265 * Retrieves the SCIM Service Provider URL.
266 *
267 * @return The SCIM Service Provider URL.
268 */
269 public URI getBaseURL() {
270 return baseURL;
271 }
272
273 /**
274 * Retrieves the content media type that should be used when writing data to
275 * the SCIM service provider.
276 *
277 * @return The content media type that should be used when writing data to
278 * the SCIM service provider.
279 */
280 public MediaType getContentType() {
281 return contentType;
282 }
283
284 /**
285 * Sets the content media type that should be used when writing data to
286 * the SCIM service provider.
287 *
288 * @param contentType he content media type that should be used when writing
289 * data to the SCIM service provider.
290 */
291 public void setContentType(final MediaType contentType) {
292 this.contentType = contentType;
293 }
294
295 /**
296 * Retrieves the accept media type that should be used when reading data from
297 * the SCIM service provider.
298 *
299 * @return The accept media type that should be used when reading data from
300 * the SCIM service provider.
301 */
302 public MediaType getAcceptType() {
303 return acceptType;
304 }
305
306 /**
307 * Sets the accept media type that should be used when reading data from
308 * the SCIM service provider.
309 *
310 * @param acceptType The accept media type that should be used when reading
311 * data from the SCIM service provider.
312 */
313 public void setAcceptType(final MediaType acceptType) {
314 this.acceptType = acceptType;
315 }
316
317 /**
318 * Retrieves the user-agent string that will be used in the HTTP request
319 * headers.
320 *
321 * @return The user-agent string. This may be null, in which case a default
322 * user-agent will be used.
323 */
324 public String getUserAgent() {
325 return userAgent;
326 }
327
328 /**
329 * Sets the user-agent string to use in the request headers.
330 *
331 * @param userAgent The user-agent string that should be used.
332 */
333 public void setUserAgent(final String userAgent) {
334 this.userAgent = userAgent;
335 }
336
337 /**
338 * Whether to override DELETE operations with POST.
339 *
340 * @return <code>true</code> to override DELETE operations with POST or
341 * <code>false</code> to use the DELETE method.
342 */
343 public boolean isOverrideDelete() {
344 return overrides[2];
345 }
346
347 /**
348 * Sets whether to override DELETE operations with POST.
349 *
350 * @param overrideDelete <code>true</code> to override DELETE operations with
351 * POST or <code>false</code> to use the DELETE method.
352 */
353 public void setOverrideDelete(final boolean overrideDelete) {
354 this.overrides[2] = overrideDelete;
355 }
356
357 /**
358 * Whether to override PATCH operations with POST.
359 *
360 * @return <code>true</code> to override PATCH operations with POST or
361 * <code>false</code> to use the PATCH method.
362 */
363 public boolean isOverridePatch() {
364 return overrides[1];
365 }
366
367 /**
368 * Sets whether to override PATCH operations with POST.
369 *
370 * @param overridePatch <code>true</code> to override PATCH operations with
371 * POST or <code>false</code> to use the PATCH method.
372 */
373 public void setOverridePatch(final boolean overridePatch) {
374 this.overrides[1] = overridePatch;
375 }
376
377 /**
378 * Whether to override PUT operations with POST.
379 *
380 * @return <code>true</code> to override PUT operations with POST or
381 * <code>false</code> to use the PUT method.
382 */
383 public boolean isOverridePut() {
384 return overrides[0];
385 }
386
387 /**
388 * Sets whether to override PUT operations with POST.
389 *
390 * @param overridePut <code>true</code> to override PUT operations with
391 * POST or <code>false</code> to use the PUT method.
392 */
393 public void setOverridePut(final boolean overridePut) {
394 this.overrides[0] = overridePut;
395 }
396 }