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

com.unboundid.scim.schema.ResourceDescriptor Maven / Gradle / Ivy

/*
 * 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.schema;

import com.unboundid.scim.data.AttributeValueResolver;
import com.unboundid.scim.data.BaseResource;
import com.unboundid.scim.data.ResourceFactory;
import com.unboundid.scim.sdk.InvalidResourceException;
import com.unboundid.scim.sdk.SCIMConstants;
import com.unboundid.scim.sdk.SCIMObject;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.unboundid.scim.sdk.StaticUtils.toLowerCase;



/**
 * This class provides methods that describe the schema for a SCIM resource. It
 * may be used to help read and write SCIM objects in their external XML and
 * JSON representation, and to convert SCIM objects to and from LDAP entries.
 */
public class ResourceDescriptor extends BaseResource
{
  /**
   * A ResourceFactory for creating ResourceDescriptor
   * instances.
   */
  public static final ResourceFactory
      RESOURCE_DESCRIPTOR_FACTORY = new ResourceFactory() {
    /**
     * {@inheritDoc}
     */
    public ResourceDescriptor createResource(
        final ResourceDescriptor resourceDescriptor,
        final SCIMObject scimObject) {
      return new ResourceDescriptor(resourceDescriptor, scimObject);
    }
  };

  /**
   * A schema -> name -> AttributeDescriptor map to quickly look up
   * attributes. The attribute descriptors are keyed by the lower case
   * attribute name because attribute names are case-insensitive. Likewise,
   * the schema key is lower case because schema URNs are case-insensitive.
   */
  private volatile Map> attributesCache;

  /**
   * Whether to use "strict mode" when looking up an attribute
   * that doesn't exist in the attributesCache. Changed to false for better
   * interoperability with SCIM implementations.
   */
  private boolean strictMode = false;

  /**
   * Constructs a new ResourceDescriptor from a existing SCIMObject.
   *
   * @param resourceDescriptor The Resource Schema descriptor.
   * @param scimObject The SCIMObject containing the schema.
   */
  ResourceDescriptor(final ResourceDescriptor resourceDescriptor,
                     final SCIMObject scimObject) {
    super(resourceDescriptor, scimObject);
  }

  /**
   * Constructs a new empty ResourceDescriptor.
   *
   * @param resourceDescriptor The Resource Schema descriptor.
   */
  private ResourceDescriptor(final ResourceDescriptor resourceDescriptor) {
    super(resourceDescriptor);
  }

  /**
   * Retrieves the attribute descriptor for a specified attribute.
   *
   * @param schema The attribute descriptor's associated schema URN.
   * @param name The name of the attribute whose descriptor is to be retrieved.
   *
   * @return The attribute descriptor for the specified attribute.
   * @throws InvalidResourceException if there is no such attribute.
   */
  public AttributeDescriptor getAttribute(final String schema,
                                          final String name)
      throws InvalidResourceException
  {
    final Map> attrCache =
        getAttributesCache();
    AttributeDescriptor attributeDescriptor = null;
    Map map = attrCache.get(toLowerCase(schema));
    if(map != null)
    {
      attributeDescriptor = map.get(toLowerCase(name));
    }
    if(attributeDescriptor == null)
    {
      if (strictMode || SCIMConstants.SCHEMA_URI_CORE.equalsIgnoreCase(schema))
      {
        throw new InvalidResourceException("Attribute " + schema +
            SCIMConstants.SEPARATOR_CHAR_QUALIFIED_ATTRIBUTE + name +
            " is not defined for resource " + getName());
      }
      else
      {
        attributeDescriptor = AttributeDescriptor.createMultiValuedAttribute(
            name, "value", null, schema, false, false, false,
            CoreSchema.createMultiValuedValueDescriptor(
                schema, AttributeDescriptor.DataType.STRING));
      }
    }
    return attributeDescriptor;
  }

  /**
   * Retrieves the attribute descriptor for a specified attribute, if the
   * attribute name is defined.  Similar to getAttribute but
   * returns null rather than throwing an exception if the attribute is
   * not defined.
   * @param schema The attribute descriptor's associated schema URI.
   * @param name The name of the attribute whose descriptor is to be retrieved.
   * @return The attribute descriptor for the specified attribute, or null
   * if the attribute is not defined on the resource.
   */
  public AttributeDescriptor findAttribute(final String schema,
                                           final String name)
  {
    final Map> attrCache =
        getAttributesCache();
    Map map = attrCache.get(toLowerCase(schema));
    return (map == null) ? null : map.get(toLowerCase(name));
  }

  /**
   * Retrieves all the attribute descriptors of the provided schema defined
   * in the resource.
   *
   * @param schema The name of the schema.
   * @return All the attribute descriptors of the provided schema defined
   * for this resource.
   */
  public Collection getAttributes(final String schema)
  {
    final Map> attrCache =
        getAttributesCache();
    Map map = attrCache.get(toLowerCase(schema));
    if(map != null)
    {
      return map.values();
    }
    return null;
  }

  /**
   * Retrieves the set of unique schemas for the attribute descriptors defined
   * in the resource.
   *
   * @return The set of unique schemas for the attribute descriptors defined
   * in the resource.
   */
  public Set getAttributeSchemas()
  {
    return getAttributesCache().keySet();
  }

  /**
   * Retrieves the schema for a specified attribute name.
   *
   * @param name The name of the attribute whose schema is to be found.
   * @param preferredSchemas an ordered list of schemas to prefer when matching
   *
   * @return The schema for the specified attribute or {@code null} if there is
   *         no such attribute name in any schema.
   */
  public String findAttributeSchema(final String name,
                                    final String... preferredSchemas)
  {
    final Map> attrCache =
        getAttributesCache();
    Set matchingSchemas = new HashSet();
    for (String schema : getAttributeSchemas())
    {
      Map map = attrCache.get(toLowerCase(schema));
      if (map!= null && map.keySet().contains(name))
      {
        matchingSchemas.add(schema);
      }
    }
    if (matchingSchemas.isEmpty())
    {
      // Element does not belong to any known schema
      return null;
    }
    List preferredSchemaList;
    if (preferredSchemas == null || preferredSchemas.length < 1)
    {
      preferredSchemaList = new ArrayList(2);
      preferredSchemaList.add(getSchema());
      preferredSchemaList.add(SCIMConstants.SCHEMA_URI_CORE);
    }
    else
    {
      preferredSchemaList = Arrays.asList(preferredSchemas);
    }
    for (String preferredSchema : preferredSchemaList)
    {
      if (matchingSchemas.contains(preferredSchema))
      {
        return preferredSchema;
      }
    }
    // If no preferred schema was found then just return the first from the list
    return matchingSchemas.iterator().next();
  }


  /**
   * Retrieve the name of the resource to be used in any external representation
   * of the resource.
   *
   * @return Retrieve the name of the resource to be used in any external
   *         representation of the resource. It is never {@code null}.
   */
  public String getName()
  {
    return getSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE, "name",
        AttributeValueResolver.STRING_RESOLVER);
  }

  /**
   * Sets the "strict mode" for this ResourceDescriptor. If strict mode is off,
   * then a call to {@link #getAttribute(String, String)} where the requested
   * attribute does not exist in the attributesCache will result in the method
   * generating an AttributeDescriptor on the fly. If strict mode were on in
   * this case, it would throw an exception because that attribute was not
   * defined.
   *
   * @param strictMode a boolean indicating whether to use strict mode or not.
   */
  public void setStrictMode(final boolean strictMode)
  {
    this.strictMode = strictMode;
  }

  /**
   * Gets the "strict mode" setting for this ResourceDescriptor. If strict mode
   * is off, then a call to {@link #getAttribute(String, String)} where the
   * requested attribute does not exist in the attributesCache will result in
   * the method generating an AttributeDescriptor on the fly. If strict mode
   * were on in this case, it would throw an exception because that attribute
   * was not defined.
   *
   * @return boolean indicating whether strict mode is enabled.
   */
  public boolean isStrictMode()
  {
    return this.strictMode;
  }

  /**
   * Sets the name of the resource to be used in any external representation
   * of the resource.
   *
   * @param name The name of the resource to be used in any external
   *             representation of the resource.
   * @return this ResourceDescriptor.
   */
  private ResourceDescriptor setName(final String name)
  {
    try {
      setSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE,
          "name", AttributeValueResolver.STRING_RESOLVER, name);
    } catch (InvalidResourceException e) {
      // This should never happen as these are core attributes...
      throw new RuntimeException(e);
    }
    return this;
  }

  /**
   * Retrieves the list of all attribute descriptors defined in the resource.
   *
   * @return The list of attribute descriptors for the resource. It is never
   *         {@code null}.
   */
  public Collection getAttributes()
  {
    Collection attributes =
        getAttributeValues(SCIMConstants.SCHEMA_URI_CORE,
            "attributes", AttributeDescriptor.ATTRIBUTE_DESCRIPTOR_RESOLVER);
    return CoreSchema.addCommonAttributes(attributes);
  }

  /**
   * Sets the list of attribute descriptors for the resource.
   *
   * @param attributes The list of attribute descriptors for the resource.
   * @return this ResourceDescriptor.
   */
  private ResourceDescriptor setAttributes(
      final Collection attributes)
  {
    try {
      setAttributeValues(SCIMConstants.SCHEMA_URI_CORE,
          "attributes", AttributeDescriptor.ATTRIBUTE_DESCRIPTOR_RESOLVER,
          attributes);
      attributesCache = null;
    } catch (InvalidResourceException e) {
      // This should never happen as these are core attributes...
      throw new RuntimeException(e);
    }
    return this;
  }

  /**
   * Returns the resource's XML schema (namespace) name.
   *
   * @return The XML namespace name.
   */
  public String getSchema()
  {
    return toLowerCase(
            getSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE, "schema",
            AttributeValueResolver.STRING_RESOLVER));
  }

  /**
   * Sets the resource's XML schema (namespace) name.
   *
   * @param schema The XML namespace name.
   * @return this ResourceDescriptor.
   */
  private ResourceDescriptor setSchema(final String schema)
  {
    try {
      setSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE,
          "schema", AttributeValueResolver.STRING_RESOLVER, schema);
    } catch (InvalidResourceException e) {
      // This should never happen as these are core attributes...
      throw new RuntimeException(e);
    }
    return this;
  }

  /**
   * Retrieves the resource's human readable description.
   *
   * @return The resource's human readable description.
   */
  public String getDescription()
  {
    return getSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE,
        "description", AttributeValueResolver.STRING_RESOLVER);
  }

  /**
   * Sets the resource's human readable description.
   *
   * @param description The resource's human readable description.
   * @return this ResourceDescriptor.
   */
  private ResourceDescriptor setDescription(final String description)
  {
    try {
      setSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE,
          "description", AttributeValueResolver.STRING_RESOLVER, description);
    } catch (InvalidResourceException e) {
      // This should never happen as these are core attributes...
      throw new RuntimeException(e);
    }
    return this;
  }

  /**
   * Retrieves the Resource's HTTP addressable endpoint relative to the
   * Base URL.
   *
   * @return The Resource's HTTP addressable endpoint relative to the Base URL.
   */
  public String getEndpoint()
  {
    return getSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE,
        "endpoint", AttributeValueResolver.STRING_RESOLVER);
  }

  /**
   * Sets the Resource's HTTP addressable endpoint relative to the
   * Base URL.
   *
   * @param endpoint The Resource's HTTP addressable endpoint relative to
   *                 the Base URL.
   * @return this ResourceDescriptor.
   */
  private ResourceDescriptor setEndpoint(final String endpoint)
  {
    try {
      setSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE,
          "endpoint", AttributeValueResolver.STRING_RESOLVER, endpoint);
    } catch (InvalidResourceException e) {
      // This should never happen as these are core attributes...
      throw new RuntimeException(e);
    }
    return this;
  }

  /**
   * Retrieves the attributesCache, using double-checked locking to avoid
   * synchronization in the common case where the cache is already initialized.
   *
   * @return A snapshot of the attributesCache. The cache is regenerated when
   *         {@link #setAttributes(java.util.Collection)} is called.
   */
  private Map> getAttributesCache()
  {
    Map> attrCache = attributesCache;
    if (attrCache == null)
    {
      synchronized (this)
      {
        attrCache = attributesCache;
        if (attrCache == null)
        {
          attrCache = new HashMap>();
          for(AttributeDescriptor attributeDescriptor : getAttributes())
          {
            final String lowerCaseSchema =
                toLowerCase(attributeDescriptor.getSchema());
            Map map =
                attrCache.get(lowerCaseSchema);
            if(map == null)
            {
              map = new HashMap();
              attrCache.put(lowerCaseSchema, map);
            }
            map.put(toLowerCase(attributeDescriptor.getName()),
                    attributeDescriptor);
          }

          attributesCache = attrCache;
        }
      }
    }
    return attrCache;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public int hashCode()
  {
    int hashCode = 31;
    hashCode += hashCode * toLowerCase(getSchema()).hashCode();
    hashCode += hashCode * toLowerCase(getName()).hashCode();
    return hashCode;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean equals(final Object obj)
  {
    if (this == obj)
    {
      return true;
    }

    if (!(obj instanceof ResourceDescriptor))
    {
      return false;
    }

    final ResourceDescriptor that = (ResourceDescriptor)obj;
    final String thisSchema = getSchema();
    final String thisName = getName();
    final String thatSchema = that.getSchema();
    final String thatName = that.getName();
    if (thisSchema == null && thatSchema == null)
    {
      return thisName.equalsIgnoreCase(thatName);
    }
    else
    {
      return thisSchema != null && thatSchema != null &&
          thisSchema.equalsIgnoreCase(thatSchema) &&
          thisName.equalsIgnoreCase(thatName);
    }
  }

  @Override
  public String toString()
  {
    return "ResourceDescriptor{" +
        "name='" + getName() + '\'' +
        ", description='" + getDescription() +
        ", schema='" + getSchema() + '\'' +
        ", endpoint='" + getEndpoint() + '\'' +
        ", attributes=" + getAttributes() +
        '}';
  }

  /**
   * Construct a new resource descriptor with the provided information.
   * The resource attributes specified here should not include common core
   * attributes (ie. id, externalId, meta) as these will be added automatically.
   *
   * @param name The addressable Resource endpoint name.
   * @param description The Resource's human readable description.
   * @param schema The Resource's associated schema URN
   * @param endpoint The Resource's HTTP addressable endpoint relative
   *                 to the Base URL.
   * @param attributes Specifies the set of associated Resource attributes.
   * @return The newly constructed resource descriptor.
   */
  public static ResourceDescriptor create(
      final String name, final String description, final String schema,
      final String endpoint, final AttributeDescriptor... attributes)
  {
    ResourceDescriptor resourceDescriptor =
      new ResourceDescriptor(CoreSchema.RESOURCE_SCHEMA_DESCRIPTOR);
    resourceDescriptor.setName(name);
    resourceDescriptor.setDescription(description);
    resourceDescriptor.setSchema(schema);
    resourceDescriptor.setEndpoint(endpoint);
    resourceDescriptor.setAttributes(Arrays.asList(attributes));

    return resourceDescriptor;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy