001/*
002 * Copyright 2015-2024 Ping Identity Corporation
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.scim2.client.requests;
019
020import com.unboundid.scim2.common.ScimResource;
021import com.unboundid.scim2.common.annotations.NotNull;
022import com.unboundid.scim2.common.annotations.Nullable;
023import com.unboundid.scim2.common.exceptions.ScimException;
024
025import jakarta.ws.rs.ProcessingException;
026import jakarta.ws.rs.client.Invocation;
027import jakarta.ws.rs.client.WebTarget;
028import jakarta.ws.rs.core.HttpHeaders;
029import jakarta.ws.rs.core.Response;
030
031/**
032 * A builder for SCIM retrieve requests.
033 */
034public abstract class RetrieveRequestBuilder
035    <T extends RetrieveRequestBuilder<T>>
036    extends ResourceReturningRequestBuilder<T>
037{
038  /**
039   * The version to match.
040   */
041  @Nullable
042  protected String version;
043
044  /**
045   * Create a new RetrieveRequestBuilder.
046   *
047   * @param target The WebTarget to GET.
048   */
049  private RetrieveRequestBuilder(@NotNull final WebTarget target)
050  {
051    super(target);
052  }
053
054  /**
055   * {@inheritDoc}
056   */
057  @Override
058  @NotNull
059  Invocation.Builder buildRequest()
060  {
061    Invocation.Builder request = super.buildRequest();
062    if(version != null)
063    {
064      request.header(HttpHeaders.IF_NONE_MATCH, version);
065    }
066    return request;
067  }
068
069
070  /**
071   * A builder for SCIM retrieve requests for where the returned resource POJO
072   * type will be the same as the original.
073   */
074  public static final class Generic<T extends ScimResource>
075      extends RetrieveRequestBuilder<Generic<T>>
076  {
077    @NotNull
078    private final T resource;
079
080    /**
081     * Create a new generic retrieve request builder.
082     *
083     * @param target The WebTarget to GET.
084     * @param resource The SCIM resource to retrieve.
085     */
086    public Generic(@NotNull final WebTarget target, @NotNull final T resource)
087    {
088      super(target);
089      this.resource = resource;
090    }
091
092    /**
093     * Retrieve the resource only if the resource has been modified from the
094     * resource provided. If the resource has not been modified, the provided
095     * resource will be returned by invoke.
096     *
097     * @return This builder.
098     */
099    @NotNull
100    public Generic<T> ifNoneMatch()
101    {
102      version = getResourceVersion(resource);
103      return this;
104    }
105
106    /**
107     * Invoke the SCIM retrieve request.
108     *
109     * @return The successfully retrieved SCIM resource or the resource provided
110     *         if the ifNoneMatch method was called and the resource has not
111     *         been modified.
112     * @throws ScimException If an error occurred.
113     */
114    @NotNull
115    @SuppressWarnings("unchecked")
116    public T invoke() throws ScimException
117    {
118      return (T) invoke(resource.getClass());
119    }
120
121    /**
122     * Invoke the SCIM modify request.
123     *
124     * @param <C> The type of object to return.
125     * @param cls The Java class object used to determine the type to return.
126     * @return The successfully modified SCIM resource.
127     * @throws ProcessingException If a JAX-RS runtime exception occurred.
128     * @throws ScimException If the SCIM service provider responded with an error.
129     */
130    @NotNull
131    public <C> C invoke(@NotNull final Class<C> cls) throws ScimException
132    {
133      Response response = buildRequest().get();
134      try
135      {
136        if(response.getStatusInfo().getFamily() ==
137            Response.Status.Family.SUCCESSFUL)
138        {
139          return response.readEntity(cls);
140        }
141        else
142        {
143          throw toScimException(response);
144        }
145      }
146      finally
147      {
148        response.close();
149      }
150    }
151  }
152
153
154  /**
155   * A builder for SCIM retrieve requests for where the returned resource POJO
156   * type will be provided.
157   */
158  public static final class Typed extends RetrieveRequestBuilder<Typed>
159  {
160    /**
161     * Create a new generic retrieve request builder.
162     *
163     * @param target The WebTarget to GET.
164     */
165    public Typed(@NotNull final WebTarget target)
166    {
167      super(target);
168    }
169
170    /**
171     * Retrieve the resource only if the resource has been modified since the
172     * provided version. If the resource has not been modified,
173     * NotModifiedException will be thrown when calling invoke.
174     *
175     * @param version The version of the resource to compare.
176     * @return This builder.
177     */
178    @NotNull
179    public Typed ifNoneMatch(@Nullable final String version)
180    {
181      this.version = version;
182      return this;
183    }
184
185    /**
186     * Invoke the SCIM retrieve request.
187     *
188     * @param <T> The type of object to return.
189     * @param cls The Java class object used to determine the type to return.
190     * @return The successfully retrieved SCIM resource.
191     * @throws ProcessingException If a JAX-RS runtime exception occurred.
192     * @throws ScimException If the SCIM service provider responded with an error.
193     */
194    @NotNull
195    public <T> T invoke(@NotNull final Class<T> cls) throws ScimException
196    {
197      Response response = buildRequest().get();
198      try
199      {
200        if(response.getStatusInfo().getFamily() ==
201            Response.Status.Family.SUCCESSFUL)
202        {
203          return response.readEntity(cls);
204        }
205        else
206        {
207          throw toScimException(response);
208        }
209      }
210      finally
211      {
212        response.close();
213      }
214    }
215  }
216}