All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.scim.marshal.json.JsonStreamMarshaller Maven / Gradle / Ivy

Go to download

The UnboundID SCIM SDK is a library that may be used to interact with various types of SCIM-enabled endpoints (such as the UnboundID server products) to perform lightweight, cloud-based identity management via the SCIM Protocol. See http://www.simplecloud.info for more information.

There is a newer version: 1.8.26
Show newest version
/*
 * Copyright 2012-2019 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */

/*
 * Copyright 2011-2019 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */

package com.unboundid.scim.marshal.json;

import com.unboundid.scim.data.BaseResource;
import com.unboundid.scim.marshal.StreamMarshaller;
import com.unboundid.scim.schema.ResourceDescriptor;
import com.unboundid.scim.sdk.BulkOperation;
import com.unboundid.scim.sdk.Debug;
import com.unboundid.scim.sdk.Resources;
import com.unboundid.scim.sdk.SCIMAttribute;
import com.unboundid.scim.sdk.SCIMAttributeValue;
import com.unboundid.scim.sdk.SCIMConstants;
import com.unboundid.scim.sdk.SCIMException;
import com.unboundid.scim.sdk.ServerErrorException;
import org.json.JSONException;
import org.json.JSONWriter;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;



/**
 * This class provides a SCIM object marshaller implementation to write a
 * stream of SCIM objects to their JSON representation.
 */
public class JsonStreamMarshaller implements StreamMarshaller
{
  private final OutputStreamWriter outputStreamWriter;
  private final JSONWriter jsonWriter;



  /**
   * Create a JSON marshaller that writes to the given output stream.
   * The resulting marshaller must be closed after use.
   *
   * @param outputStream  The ouput stream to write to.
   *
   * @throws SCIMException  If the marshaller could not be created.
   */
  public JsonStreamMarshaller(final OutputStream outputStream)
      throws SCIMException
  {
    try
    {
      outputStreamWriter = new OutputStreamWriter(outputStream);
      jsonWriter = new JSONWriter(outputStreamWriter);
    }
    catch (Exception e)
    {
      Debug.debugException(e);
      throw new ServerErrorException(
          "Cannot create JSON marshaller: " + e.getMessage());
    }
  }



  /**
   * {@inheritDoc}
   */
  @Override
  public void close()
      throws SCIMException
  {
    try
    {
      outputStreamWriter.close();
    }
    catch (IOException e)
    {
      Debug.debugException(e);
      throw new ServerErrorException(
          "Cannot close marshaller: " + e.getMessage());
    }
  }



  /**
   * {@inheritDoc}
   */
  public void marshal(final BaseResource resource)
      throws SCIMException
  {
    boolean includeSchemas = !(resource instanceof ResourceDescriptor);
    try
    {
      marshal(resource, includeSchemas);
    }
    catch (JSONException e)
    {
      Debug.debugException(e);
      throw new ServerErrorException(
          "Cannot write resource: " + e.getMessage());
    }
  }



  /**
   * Write a SCIM resource to a JSON writer.
   *
   * @param resource   The SCIM resource to be written.
   * @param includeSchemas  Indicates whether the schemas should be written
   *                        at the start of the object.
   * @throws org.json.JSONException Thrown if error writing to output.
   */
  private void marshal(final BaseResource resource,
                       final boolean includeSchemas)
      throws JSONException
  {
    jsonWriter.object();

    final Set schemas = new HashSet(
        resource.getResourceDescriptor().getAttributeSchemas());
    if (includeSchemas)
    {
      // Write out the schemas for this object.
      jsonWriter.key(SCIMConstants.SCHEMAS_ATTRIBUTE_NAME);
      jsonWriter.array();
      for (final String schema : schemas)
      {
        jsonWriter.value(schema);
      }
      jsonWriter.endArray();
    }

    // first write out core schema, then if any extensions write them
    // out in their own json object keyed by the schema name

    for (final SCIMAttribute attribute : resource.getScimObject()
        .getAttributes(SCIMConstants.SCHEMA_URI_CORE))
    {
      if (attribute.getAttributeDescriptor().isMultiValued())
      {
        this.writeMultiValuedAttribute(attribute, jsonWriter);
      }
      else
      {
        this.writeSingularAttribute(attribute, jsonWriter);
      }
    }

    // write out any custom schemas
    for (final String schema : schemas)
    {
      if (!schema.equalsIgnoreCase(SCIMConstants.SCHEMA_URI_CORE))
      {
        Collection attributes =
            resource.getScimObject().getAttributes(schema);
        if(!attributes.isEmpty())
        {
          jsonWriter.key(schema);
          jsonWriter.object();
          for (SCIMAttribute attribute : attributes)
          {
            if (attribute.getAttributeDescriptor().isMultiValued())
            {
              this.writeMultiValuedAttribute(attribute, jsonWriter);
            }
            else
            {
              this.writeSingularAttribute(attribute, jsonWriter);
            }
          }
          jsonWriter.endObject();
        }
      }
    }
    jsonWriter.endObject();
  }

  /**
   * {@inheritDoc}
   */
  public void marshal(final Resources response)
      throws SCIMException
  {
    try
    {
      jsonWriter.object();
      jsonWriter.key("totalResults");
      jsonWriter.value(response.getTotalResults());

      jsonWriter.key("itemsPerPage");
      jsonWriter.value(response.getItemsPerPage());

      jsonWriter.key("startIndex");
      jsonWriter.value(response.getStartIndex());

      // Figure out what schemas are referenced by the resources.
      final Set schemaURIs = new HashSet();
      for (final BaseResource resource : response)
      {
        schemaURIs.addAll(
            resource.getResourceDescriptor().getAttributeSchemas());
      }

      // Write the schemas.
      jsonWriter.key(SCIMConstants.SCHEMAS_ATTRIBUTE_NAME);
      jsonWriter.array();
      for (final String schemaURI : schemaURIs)
      {
        jsonWriter.value(schemaURI);
      }
      jsonWriter.endArray();

      // Write the resources.
      jsonWriter.key("Resources");
      jsonWriter.array();
      for (final BaseResource resource : response)
      {
        marshal(resource, false);
      }
      jsonWriter.endArray();

      jsonWriter.endObject();
    }
    catch (JSONException e)
    {
      Debug.debugException(e);
      throw new ServerErrorException(
          "Cannot write resources response: " + e.getMessage());
    }
  }


  /**
   * {@inheritDoc}
   */
  public void marshal(final SCIMException response)
      throws SCIMException
  {
    try
    {
      jsonWriter.object();
      jsonWriter.key("Errors");
      jsonWriter.array();

      jsonWriter.object();

      jsonWriter.key("code");
      jsonWriter.value(String.valueOf(response.getStatusCode()));

      final String description = response.getMessage();
      if (description != null)
      {
        jsonWriter.key("description");
        jsonWriter.value(description);
      }

      jsonWriter.endObject();

      jsonWriter.endArray();

      jsonWriter.endObject();
    }
    catch (JSONException e)
    {
      Debug.debugException(e);
      throw new ServerErrorException(
          "Cannot write error response: " + e.getMessage());
    }
  }



  /**
   * {@inheritDoc}
   */
  public void writeBulkStart(final int failOnErrors,
                             final Set schemaURIs)
      throws SCIMException
  {
    try
    {
      jsonWriter.object();

      if (failOnErrors >= 0)
      {
        jsonWriter.key("failOnErrors");
        jsonWriter.value(failOnErrors);
      }

      // Write the schemas.
      jsonWriter.key(SCIMConstants.SCHEMAS_ATTRIBUTE_NAME);
      jsonWriter.array();
      for (final String schemaURI : schemaURIs)
      {
        jsonWriter.value(schemaURI);
      }
      jsonWriter.endArray();

      // Write the operations.
      jsonWriter.key("Operations");
      jsonWriter.array();
    }
    catch (JSONException e)
    {
      Debug.debugException(e);
      throw new ServerErrorException(
          "Cannot write start of bulk operations: " + e.getMessage());
    }
  }



  /**
   * {@inheritDoc}
   */
  public void writeBulkOperation(final BulkOperation o)
      throws SCIMException
  {
    try
    {
      jsonWriter.object();
      if (o.getMethod() != null)
      {
        jsonWriter.key("method");
        jsonWriter.value(o.getMethod());
      }
      if (o.getBulkId() != null)
      {
        jsonWriter.key("bulkId");
        jsonWriter.value(o.getBulkId());
      }
      if (o.getVersion() != null)
      {
        jsonWriter.key("version");
        jsonWriter.value(o.getVersion());
      }
      if (o.getPath() != null)
      {
        jsonWriter.key("path");
        jsonWriter.value(o.getPath());
      }
      if (o.getLocation() != null)
      {
        jsonWriter.key("location");
        jsonWriter.value(o.getLocation());
      }
      if (o.getData() != null)
      {
        jsonWriter.key("data");
        marshal(o.getData(), true);
      }
      if (o.getStatus() != null)
      {
        jsonWriter.key("status");
        jsonWriter.object();
        jsonWriter.key("code");
        jsonWriter.value(o.getStatus().getCode());
        if (o.getStatus().getDescription() != null)
        {
          jsonWriter.key("description");
          jsonWriter.value(o.getStatus().getDescription());
        }
        jsonWriter.endObject();
      }
      jsonWriter.endObject();
    }
    catch (JSONException e)
    {
      Debug.debugException(e);
      throw new ServerErrorException(
          "Cannot write bulk operation: " + e.getMessage());
    }
  }


  /**
   * {@inheritDoc}
   */
  public void writeBulkFinish()
      throws SCIMException
  {
    try
    {
      jsonWriter.endArray();
      jsonWriter.endObject();
    }
    catch (JSONException e)
    {
      Debug.debugException(e);
      throw new ServerErrorException(
          "Cannot write end of bulk operations: " + e.getMessage());
    }
  }



  /**
   * {@inheritDoc}
   */
  public void bulkMarshal(final int failOnErrors,
                          final List operations)
      throws SCIMException
  {
    // Figure out what schemas are referenced by the resources.
    final Set schemaURIs = new HashSet();
    for (final BulkOperation o : operations)
    {
      final BaseResource resource = o.getData();
      if (resource != null)
      {
        schemaURIs.addAll(
            o.getData().getResourceDescriptor().getAttributeSchemas());
      }
    }

    writeBulkStart(failOnErrors, schemaURIs);
    for (final BulkOperation o : operations)
    {
      writeBulkOperation(o);
    }
    writeBulkFinish();
  }



  /**
   * Write a multi-valued attribute to an XML stream.
   *
   * @param scimAttribute The attribute to be written.
   * @param jsonWriter    Output to write the attribute to.
   *
   * @throws JSONException Thrown if error writing to output.
   */
  private void writeMultiValuedAttribute(final SCIMAttribute scimAttribute,
                                         final JSONWriter jsonWriter)
      throws JSONException
  {

    SCIMAttributeValue[] values = scimAttribute.getValues();
    jsonWriter.key(scimAttribute.getName());
    jsonWriter.array();
    for (SCIMAttributeValue value : values)
    {
      if (value == null)
      {
        continue;
      }

      if (value.isComplex())
      {
        jsonWriter.object();
        for (SCIMAttribute attribute : value.getAttributes().values())
        {
          if (attribute.getAttributeDescriptor().isMultiValued())
          {
            this.writeMultiValuedAttribute(attribute, jsonWriter);
          }
          else
          {
            this.writeSingularAttribute(attribute, jsonWriter);
          }
        }
        jsonWriter.endObject();
      }
      else
      {
        if (scimAttribute.getAttributeDescriptor().getDataType() != null)
        {
          switch (scimAttribute.getAttributeDescriptor().getDataType())
          {
            case BOOLEAN:
              jsonWriter.value(value.getBooleanValue());
              break;

            case DECIMAL:
              jsonWriter.value(value.getDecimalValue());
              break;

            case INTEGER:
              jsonWriter.value(value.getIntegerValue());
              break;

            case BINARY:
            case DATETIME:
            case STRING:
            default:
              jsonWriter.value(value.getStringValue());
              break;
          }
        }
        else
        {
          jsonWriter.value(value.getStringValue());
        }
      }
    }
    jsonWriter.endArray();
  }



  /**
   * Write a singular attribute to an XML stream.
   *
   * @param scimAttribute The attribute to be written.
   * @param jsonWriter    Output to write the attribute to.
   *
   * @throws org.json.JSONException Thrown if error writing to output.
   */
  private void writeSingularAttribute(final SCIMAttribute scimAttribute,
                                      final JSONWriter jsonWriter)
      throws JSONException
  {
    jsonWriter.key(scimAttribute.getName());
    SCIMAttributeValue val = scimAttribute.getValue();
    if (val.isComplex())
    {
      jsonWriter.object();
      for (SCIMAttribute a : val.getAttributes().values())
      {
        if (a.getAttributeDescriptor().isMultiValued())
        {
          this.writeMultiValuedAttribute(a, jsonWriter);
        }
        else
        {
          this.writeSingularAttribute(a, jsonWriter);
        }
      }
      jsonWriter.endObject();
    }
    else
    {
      if (scimAttribute.getAttributeDescriptor().getDataType() != null)
      {
        switch (scimAttribute.getAttributeDescriptor().getDataType())
        {
          case BOOLEAN:
            jsonWriter.value(val.getBooleanValue());
            break;

          case DECIMAL:
            jsonWriter.value(val.getDecimalValue());
            break;

          case INTEGER:
            jsonWriter.value(val.getIntegerValue());
            break;

          case BINARY:
          case DATETIME:
          case STRING:
          default:
            jsonWriter.value(val.getStringValue());
            break;
        }
      }
      else
      {
        jsonWriter.value(val.getStringValue());
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy