001/*
002 * Copyright 2011-2015 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
018package com.unboundid.scim.sdk;
019
020import com.unboundid.scim.data.BaseResource;
021import com.unboundid.scim.data.Meta;
022import com.unboundid.scim.schema.CoreSchema;
023import com.unboundid.scim.schema.ResourceDescriptor;
024import com.unboundid.scim.wink.SCIMApplication;
025
026import javax.ws.rs.core.UriBuilder;
027import java.util.ArrayList;
028import java.util.Collection;
029import java.util.Collections;
030import java.util.List;
031
032/**
033 * This class provides an implementation of the SCIM server backend API that
034 * serves up the resource schema from a collection of ResourceDescriptors.
035 */
036public class ResourceSchemaBackend extends SCIMBackend
037{
038  private final SCIMApplication application;
039
040  /**
041   * Create a new ResourceSchemaBackend that serves up the schema provided
042   * from the ResourceDescriptors.
043   *
044   * @param application         The SCIM JAX-RS application associated with this
045   *                            backend.
046   */
047  public ResourceSchemaBackend(final SCIMApplication application) {
048    this.application = application;
049  }
050
051  /**
052   * {@inheritDoc}
053   */
054  @Override
055  public void finalizeBackend() {
056    // Nothing to do.
057  }
058
059  /**
060   * {@inheritDoc}
061   */
062  @Override
063  public ResourceDescriptor getResource(final GetResourceRequest request)
064      throws SCIMException {
065    ResourceDescriptor resourceDescriptor = null;
066    for(ResourceDescriptor rd :
067        application.getBackend().getResourceDescriptors())
068    {
069      String id = rd.getSchema() +
070          SCIMConstants.SEPARATOR_CHAR_QUALIFIED_ATTRIBUTE + rd.getName();
071      if(id.equalsIgnoreCase(request.getResourceID()))
072      {
073        resourceDescriptor = rd;
074        break;
075      }
076    }
077
078    // Try to find a match in case the schema name was not provided.
079    if (resourceDescriptor == null)
080    {
081      for(ResourceDescriptor rd :
082          application.getBackend().getResourceDescriptors())
083      {
084        if(rd.getName().equalsIgnoreCase(request.getResourceID()))
085        {
086          resourceDescriptor = rd;
087          break;
088        }
089      }
090    }
091
092    if(resourceDescriptor == null)
093    {
094      throw new ResourceNotFoundException("No Resource Schema with ID " +
095          request.getResourceID() + " exists");
096    }
097
098    return copyAndSetIdAndMetaAttributes(resourceDescriptor, request);
099  }
100
101  /**
102   * {@inheritDoc}
103   */
104  @Override
105  public Resources getResources(final GetResourcesRequest request)
106      throws SCIMException {
107    List<BaseResource> rds =
108        new ArrayList<BaseResource>(
109            application.getBackend().getResourceDescriptors().size());
110
111    for(ResourceDescriptor resourceDescriptor :
112        application.getBackend().getResourceDescriptors())
113    {
114      ResourceDescriptor copy =
115          copyAndSetIdAndMetaAttributes(resourceDescriptor, request);
116      if(request.getFilter() == null ||
117          copy.getScimObject().matchesFilter(request.getFilter()))
118      {
119        rds.add(copy);
120      }
121    }
122
123    int fromIndex = 0;
124    int total = rds.size();
125    if(request.getPageParameters() != null)
126    {
127      fromIndex = request.getPageParameters().getStartIndex() - 1;
128      int endIndex =
129          Math.min(request.getPageParameters().getCount() + fromIndex,
130                   rds.size());
131      rds = rds.subList(fromIndex, endIndex);
132    }
133
134    return new Resources<BaseResource>(rds, total, fromIndex + 1);
135  }
136
137  /**
138   * {@inheritDoc}
139   */
140  @Override
141  public BaseResource postResource(final PostResourceRequest request)
142      throws SCIMException {
143    throw new UnsupportedOperationException("POST operations are not allowed " +
144        "on the Resource Schema");
145  }
146
147  /**
148   * {@inheritDoc}
149   */
150  @Override
151  public void deleteResource(final DeleteResourceRequest request)
152      throws SCIMException {
153    throw new UnsupportedOperationException("DELETE operations are not " +
154        "allowed on the Resource Schema");
155  }
156
157  /**
158   * {@inheritDoc}
159   */
160  @Override
161  public BaseResource putResource(final PutResourceRequest request)
162      throws SCIMException {
163    throw new UnsupportedOperationException("PUT operations are not allowed " +
164        "on the Resource Schema");
165  }
166
167  /**
168   * {@inheritDoc}
169   */
170  @Override
171  public BaseResource patchResource(final PatchResourceRequest request)
172      throws SCIMException {
173    throw new UnsupportedOperationException("PATCH operations are not " +
174            "allowed on the Resource Schema");
175  }
176
177  /**
178   * {@inheritDoc}
179   */
180  @Override
181  public Collection<ResourceDescriptor> getResourceDescriptors()
182  {
183    return Collections.singleton(CoreSchema.RESOURCE_SCHEMA_DESCRIPTOR);
184  }
185
186  /**
187   * {@inheritDoc}
188   */
189  @Override
190  public void getStreamedResources(
191      final GetStreamedResourcesRequest request,
192      final StreamedResultListener listener) throws SCIMException
193  {
194    throw new UnsupportedOperationException(
195        "Streamed Query not supported on Schema endpoint");
196  }
197
198  /**
199   * Make a copy of the ResourceDescriptor and set the id and meta attributes
200   * from the provided information.
201   *
202   * @param resource  The SCIM object whose id and meta attributes are to be
203   *                    set.
204   * @param request   The SCIM request.
205   * @return          The copy of the ResourceDescriptor.
206   */
207  public static ResourceDescriptor copyAndSetIdAndMetaAttributes(
208      final ResourceDescriptor resource,
209      final ResourceReturningRequest request)
210  {
211    ResourceDescriptor copy =
212        ResourceDescriptor.RESOURCE_DESCRIPTOR_FACTORY.createResource(
213            resource.getResourceDescriptor(),
214            new SCIMObject(resource.getScimObject()));
215
216    String id = resource.getSchema() +
217        SCIMConstants.SEPARATOR_CHAR_QUALIFIED_ATTRIBUTE + resource.getName();
218    copy.setId(id);
219
220    final UriBuilder uriBuilder = UriBuilder.fromUri(request.getBaseURL());
221    if (!request.getBaseURL().getPath().endsWith("v1/"))
222    {
223      uriBuilder.path("v1");
224    }
225    uriBuilder.path(resource.getResourceDescriptor().getEndpoint());
226    uriBuilder.path(id);
227
228    copy.setMeta(new Meta(null, null, uriBuilder.build(), null));
229
230    // Pare down the attributes to those requested.
231    return ResourceDescriptor.RESOURCE_DESCRIPTOR_FACTORY.createResource(
232        resource.getResourceDescriptor(),
233        request.getAttributes().pareObject(copy.getScimObject()));
234  }
235}