001/*
002 * Copyright 2011-2013 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 boolean authenticate(final String userID, final String password) {
056    return false;
057  }
058
059  /**
060   * {@inheritDoc}
061   */
062  @Override
063  public void finalizeBackend() {
064    // Nothing to do.
065  }
066
067  /**
068   * {@inheritDoc}
069   */
070  @Override
071  public ResourceDescriptor getResource(final GetResourceRequest request)
072      throws SCIMException {
073    ResourceDescriptor resourceDescriptor = null;
074    for(ResourceDescriptor rd :
075        application.getBackend().getResourceDescriptors())
076    {
077      String id = rd.getSchema() +
078          SCIMConstants.SEPARATOR_CHAR_QUALIFIED_ATTRIBUTE + rd.getName();
079      if(id.equalsIgnoreCase(request.getResourceID()))
080      {
081        resourceDescriptor = rd;
082        break;
083      }
084    }
085
086    // Try to find a match in case the schema name was not provided.
087    if (resourceDescriptor == null)
088    {
089      for(ResourceDescriptor rd :
090          application.getBackend().getResourceDescriptors())
091      {
092        if(rd.getName().equalsIgnoreCase(request.getResourceID()))
093        {
094          resourceDescriptor = rd;
095          break;
096        }
097      }
098    }
099
100    if(resourceDescriptor == null)
101    {
102      throw new ResourceNotFoundException("No Resource Schema with ID " +
103          request.getResourceID() + " exists");
104    }
105
106    return copyAndSetIdAndMetaAttributes(resourceDescriptor, request);
107  }
108
109  /**
110   * {@inheritDoc}
111   */
112  @Override
113  public Resources getResources(final GetResourcesRequest request)
114      throws SCIMException {
115    List<BaseResource> rds =
116        new ArrayList<BaseResource>(
117            application.getBackend().getResourceDescriptors().size());
118
119    for(ResourceDescriptor resourceDescriptor :
120        application.getBackend().getResourceDescriptors())
121    {
122      ResourceDescriptor copy =
123          copyAndSetIdAndMetaAttributes(resourceDescriptor, request);
124      if(request.getFilter() == null ||
125          copy.getScimObject().matchesFilter(request.getFilter()))
126      {
127        rds.add(copy);
128      }
129    }
130
131    int fromIndex = 0;
132    int total = rds.size();
133    if(request.getPageParameters() != null)
134    {
135      fromIndex = request.getPageParameters().getStartIndex() - 1;
136      int endIndex =
137          Math.min(request.getPageParameters().getCount() + fromIndex,
138                   rds.size());
139      rds = rds.subList(fromIndex, endIndex);
140    }
141
142    return new Resources<BaseResource>(rds, total, fromIndex + 1);
143  }
144
145  /**
146   * {@inheritDoc}
147   */
148  @Override
149  public BaseResource postResource(final PostResourceRequest request)
150      throws SCIMException {
151    throw new UnsupportedOperationException("POST operations are not allowed " +
152        "on the Resource Schema");
153  }
154
155  /**
156   * {@inheritDoc}
157   */
158  @Override
159  public void deleteResource(final DeleteResourceRequest request)
160      throws SCIMException {
161    throw new UnsupportedOperationException("DELETE operations are not " +
162        "allowed on the Resource Schema");
163  }
164
165  /**
166   * {@inheritDoc}
167   */
168  @Override
169  public BaseResource putResource(final PutResourceRequest request)
170      throws SCIMException {
171    throw new UnsupportedOperationException("PUT operations are not allowed " +
172        "on the Resource Schema");
173  }
174
175  /**
176   * {@inheritDoc}
177   */
178  @Override
179  public BaseResource patchResource(final PatchResourceRequest request)
180      throws SCIMException {
181    throw new UnsupportedOperationException("PATCH operations are not " +
182            "allowed on the Resource Schema");
183  }
184
185  @Override
186  public Collection<ResourceDescriptor> getResourceDescriptors()
187  {
188    return Collections.singleton(CoreSchema.RESOURCE_SCHEMA_DESCRIPTOR);
189  }
190
191  /**
192   * Make a copy of the ResourceDescriptor and set the id and meta attributes
193   * from the provided information.
194   *
195   * @param resource  The SCIM object whose id and meta attributes are to be
196   *                    set.
197   * @param request   The SCIM request.
198   * @return          The copy of the ResourceDescriptor.
199   */
200  public static ResourceDescriptor copyAndSetIdAndMetaAttributes(
201      final ResourceDescriptor resource,
202      final ResourceReturningRequest request)
203  {
204    ResourceDescriptor copy =
205        ResourceDescriptor.RESOURCE_DESCRIPTOR_FACTORY.createResource(
206            resource.getResourceDescriptor(),
207            new SCIMObject(resource.getScimObject()));
208
209    String id = resource.getSchema() +
210        SCIMConstants.SEPARATOR_CHAR_QUALIFIED_ATTRIBUTE + resource.getName();
211    copy.setId(id);
212
213    final UriBuilder uriBuilder = UriBuilder.fromUri(request.getBaseURL());
214    if (!request.getBaseURL().getPath().endsWith("v1/"))
215    {
216      uriBuilder.path("v1");
217    }
218    uriBuilder.path(resource.getResourceDescriptor().getEndpoint());
219    uriBuilder.path(id);
220
221    copy.setMeta(new Meta(null, null, uriBuilder.build(), null));
222
223    // Pare down the attributes to those requested.
224    return ResourceDescriptor.RESOURCE_DESCRIPTOR_FACTORY.createResource(
225        resource.getResourceDescriptor(),
226        request.getAttributes().pareObject(copy.getScimObject()));
227  }
228}