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

edu.internet2.middleware.subject.provider.SubjectImpl Maven / Gradle / Ivy

There is a newer version: 5.12.2
Show newest version
/**
 * Copyright 2014 Internet2
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*--
$Id: SubjectImpl.java,v 1.1 2009-09-02 05:40:10 mchyzer Exp $
$Date: 2009-09-02 05:40:10 $
 
Copyright 2005 Internet2 and Stanford University.  All Rights Reserved.
See doc/license.txt in this distribution.
 */
package edu.internet2.middleware.subject.provider;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.collections.map.CaseInsensitiveMap;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import edu.internet2.middleware.grouper.subj.GrouperJdbcSourceAdapter2_5;
import edu.internet2.middleware.grouper.subj.GrouperLdapSourceAdapter2_5;
import edu.internet2.middleware.grouper.util.GrouperUtil;
import edu.internet2.middleware.grouperClient.util.ExpirableCache;
import edu.internet2.middleware.subject.Source;
import edu.internet2.middleware.subject.Subject;
import edu.internet2.middleware.subject.SubjectCaseInsensitiveMap;
import edu.internet2.middleware.subject.SubjectCaseInsensitiveMapImpl;
import edu.internet2.middleware.subject.SubjectCaseInsensitiveSet;
import edu.internet2.middleware.subject.SubjectCaseInsensitiveSetImpl;
import edu.internet2.middleware.subject.SubjectType;
import edu.internet2.middleware.subject.SubjectUtils;
import edu.internet2.middleware.subject.config.SubjectConfig;

/**
 * Base Subject implementation.  Subclass this to change behavior
 */
@SuppressWarnings("serial")
public class SubjectImpl implements Subject {

  /**
   * turn some strings into a map, every other is a name or value of attribute
   * @param strings
   * @return the map (never null)
   */
  public static Map> toAttributeMap(String... strings) {
    Map> map = new SubjectCaseInsensitiveMapImpl>();
    if (strings != null) {
      if (strings.length % 2 != 0) {
        throw new RuntimeException("Must pass in an odd number of strings: " + strings.length);
      }
      for (int i=0;i set = new LinkedHashSet();
        set.add(strings[i+1]);
        map.put(strings[i], set);
      }
    }
    return map;
  }
  
  /**
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString() {
    return toStringStatic(this);
  }


  /**
   * toString
   * @param subject
   * @return string
   */
  public static String toStringStatic(Subject subject) {
    String name = subject.getName();
    return "Subject id: " + subject.getId() + ", sourceId: " + subject.getSourceId() + 
      (StringUtils.isBlank(name) ? "" : (", name: " + name));
  }

  /** */
  private static Log log = edu.internet2.middleware.grouper.util.GrouperUtil.getLog(SubjectImpl.class);

  /** */
  private String id;

  /** */
  private String typeName;

  
  /**
   * sourceId
   * @return the sourceId
   */
  public String getSourceId() {
    return this.sourceId;
  }

  
  /**
   * sourceId
   * @param sourceId1 the sourceId to set
   */
  public void setSourceId(String sourceId1) {
    this.sourceId = sourceId1;
  }

  /** sourceId */
  private String sourceId;
  
  /** nameAttribute */
  private String nameAttribute;
  
  /** descriptionAttribute */
  private String descriptionAttribute;
  
  /** nameOverride */
  private String nameOverride;
  
  /** descriptionOverride */
  private String descriptionOverride;
  
  /** */
  private Map> attributes = null;
  
  /** */
  private boolean isResolvedFromSource = false;
  
  /**
   * Constructor called by SourceManager.  Will create an empty map for attributes
   * @param id1 
   * @param name1 
   * @param description1 
   * @param typeName1 
   * @param sourceId1 
   */
  public SubjectImpl(String id1, String name1, String description1, String typeName1,
      String sourceId1) {
    this(id1, name1, description1, typeName1, sourceId1, new SubjectCaseInsensitiveMapImpl>());
  }

  /**
   * Constructor called by SourceManager.
   * @param id1 
   * @param name1 
   * @param description1 
   * @param typeName1 
   * @param sourceId1 
   * @param attributes1 
   */
  public SubjectImpl(String id1, String name1, String description1, String typeName1,
      String sourceId1, Map> attributes1) {
    this.id = id1;
    this.nameOverride = name1;
    this.typeName = typeName1;
    this.descriptionOverride = description1;
    this.sourceId = sourceId1;
    this.setAttributes(attributes1);
  }

  private SubjectImpl() {
    
  }
  
  /**
   * 
   * @param subject
   * @return the cloned subject
   */
  public static Subject cloneSubject(Subject subject) {

    if (subject == null) {
      return null;
    }
    
    if (subject instanceof SubjectImpl) {
      
      SubjectImpl clonedSubject = new SubjectImpl(); 
      clonedSubject.id = ((SubjectImpl)subject).id;
      clonedSubject.typeName = ((SubjectImpl)subject).typeName;
      clonedSubject.sourceId = ((SubjectImpl)subject).sourceId;
      clonedSubject.nameAttribute = ((SubjectImpl)subject).nameAttribute;
      clonedSubject.descriptionAttribute = ((SubjectImpl)subject).descriptionAttribute;
      clonedSubject.nameOverride = ((SubjectImpl)subject).nameOverride;
      clonedSubject.descriptionOverride = ((SubjectImpl)subject).descriptionOverride;
      clonedSubject.attributes = 
          new SubjectCaseInsensitiveMapImpl>(GrouperUtil.nonNull(((SubjectImpl)subject).attributes));
      clonedSubject.translationMap = new CaseInsensitiveMap(GrouperUtil.nonNull(((SubjectImpl)subject).translationMap));
      clonedSubject.attributesInitted = false;
      return clonedSubject;
    }
    
    Subject clonedSubject = new SubjectImpl(subject.getId(), subject.getName(), 
        subject.getDescription(), subject.getTypeName(), subject.getSourceId(), 
        new SubjectCaseInsensitiveMapImpl>(GrouperUtil.nonNull(subject.getAttributes(false))));
    clonedSubject.setTranslationMap(new CaseInsensitiveMap(GrouperUtil.nonNull(subject.getTranslationMap())));
    return clonedSubject;
  }

  /**
   * Constructor called by SourceManager.  Will create an empty map for attributes
   * @param id1 
   * @param name1 
   * @param description1 
   * @param typeName1 
   * @param sourceId1 
   * @param nameAttribute1 
   * @param descriptionAttribute1 
   */
  public SubjectImpl(String id1, String name1, String description1, String typeName1,
      String sourceId1, String nameAttribute1, String descriptionAttribute1) {
    this(id1, name1, description1, typeName1, sourceId1, new SubjectCaseInsensitiveMapImpl>(), 
        nameAttribute1, descriptionAttribute1);
  }

  /**
   * Constructor called by SourceManager.
   * @param id1 
   * @param name1 
   * @param description1 
   * @param typeName1 
   * @param sourceId1 
   * @param attributes1 
   * @param nameAttribute1 
   * @param descriptionAttribute1 
   */
  public SubjectImpl(String id1, String name1, String description1, String typeName1,
      String sourceId1, Map> attributes1,
      String nameAttribute1, String descriptionAttribute1) {
    this.id = id1;
    this.typeName = typeName1;
    this.sourceId = sourceId1;
    this.nameAttribute = nameAttribute1;
    this.descriptionAttribute = descriptionAttribute1;
    this.nameOverride = name1;
    this.descriptionOverride = description1;
    this.setAttributes(attributes1);
  }

  /**
   * init new style virtual (translated) attributes
   */
  private void initVirtualAttributes() {
    
    Map virtualAttributes = BaseSourceAdapter.virtualAttributesForSource(this.getSource());

    if (GrouperUtil.length(virtualAttributes) == 0) {
      return;
    }

    // remove the virtual attributes
    for (String virtualAttributeName : virtualAttributes.keySet()) {
      this.translationMap.remove(virtualAttributeName);
    }
    
    //do these in order of config
    for (String virtualAttributeName : virtualAttributes.keySet()) {
      
      String translation = virtualAttributes.get(virtualAttributeName);
      
      String value = runScriptStatic(translation, this.translationMap);
      this.attributes.put(virtualAttributeName, GrouperUtil.toSet(value));
      this.translationMap.put("subject_attribute__" + virtualAttributeName.toLowerCase(), value);
      
    }
  }
  
  public static String runScriptStatic(String script, Map translationMap) {
    
    Object valueObject = GrouperUtil.substituteExpressionLanguageScript(script, translationMap, true, false, true);
    String value = GrouperUtil.stringValue(valueObject);
    return value;
  }
  

  /**
   * {@inheritDoc}
   */
  public String getId() {
    return this.id;
  }

  /**
   * {@inheritDoc}
   */
  public SubjectType getType() {
    return SubjectTypeEnum.valueOf(this.typeName);
  }

  /**
   * {@inheritDoc}
   */
  public String getName() {
    
    if (nameOverride != null) {
      return nameOverride;
    }
    
    if (StringUtils.isBlank(nameAttribute)) {
      return null;
    }
    
    if (attributes != null && attributes.containsKey(nameAttribute) && GrouperUtil.length(attributes.get(nameAttribute)) > 0) {
      return attributes.get(nameAttribute).iterator().next();
    }
    
    // ok in case it's a virtual attribute and hasn't been init'ed
    return getAttributeValue(nameAttribute, false);
  }

  /**
   * {@inheritDoc}
   */
  public String getDescription() {
    
    if (descriptionOverride != null) {
      return descriptionOverride;
    }
    
    if (StringUtils.isBlank(descriptionAttribute)) {
      return null;
    }
    
    if (attributes != null && attributes.containsKey(descriptionAttribute) && GrouperUtil.length(attributes.get(descriptionAttribute)) > 0) {
      return attributes.get(descriptionAttribute).iterator().next();
    }
    
    // ok in case it's a virtual attribute and hasn't been init'ed
    return getAttributeValue(descriptionAttribute, false);
  }

  /**
   * @see edu.internet2.middleware.subject.Subject#getAttributeValue(java.lang.String, boolean)
   */
  public String getAttributeValue(String name1) {
    return getAttributeValue(name1, true);
  }
  
  /**
   * @see edu.internet2.middleware.subject.Subject#getAttributeValue(java.lang.String, boolean)
   */
  public String getAttributeValue(String name1, boolean excludeInternalAttributes) {
    this.initAttributesIfNeeded();
    if (this.attributes == null) {
      return null;
    }
    
    if (excludeInternalAttributes && !StringUtils.isBlank(this.sourceId) && getSource().getInternalAttributes().contains(name1)) {
      return null;
    }
    
    Set values = this.attributes.get(name1);
    if (values != null && values.size() > 0) {
      //return the first, no matter how many there are
      return values.iterator().next();
    }
    return null;
  }

  /**
   * @see edu.internet2.middleware.subject.Subject#getAttributeValues(java.lang.String)
   */
  public Set getAttributeValues(String name1) {
    return getAttributeValues(name1, true);
  }
  
  /**
   * @see edu.internet2.middleware.subject.Subject#getAttributeValues(java.lang.String, boolean)
   */
  public Set getAttributeValues(String name1, boolean excludeInternalAttributes) {
    this.initAttributesIfNeeded();
    if (this.attributes == null) {
      return new LinkedHashSet();
    }
    
    if (excludeInternalAttributes && !StringUtils.isBlank(this.sourceId) && getSource().getInternalAttributes().contains(name1)) {
      return null;
    }
    
    return this.attributes.get(name1);
  }

  /** if we have initted the attributes */
  private boolean attributesInitted = false;
  
  /**
   * 
   */
  private Map translationMap;
  
  
  public Map getTranslationMap() {
    return translationMap;
  }

  
  public void setTranslationMap(Map translationMap) {
    this.translationMap = translationMap;
  }

  /**
   * 
   */
  private void initAttributesIfNeeded() {
    
    //if no source, cant init
    //maybe we are just creating the subject or something...
    if (StringUtils.isBlank(this.sourceId)) {
      return;
    }
    
    if (!this.attributesInitted) {
      synchronized(this) {
        if (!this.attributesInitted) {
          //NOTE we get endless loops without this
          this.attributesInitted = true;
          
          // legacy
          this.initVirtualAttributesLegacy();
          this.initVirtualAttributes();
        }
      }
    }
  }

  /**
   * clear the state for attributes initted, i.e. recalculated them
   */
  public void attributesInittedClear() {
    this.attributesInitted = false;
    
    Map virtualAttributes = BaseSourceAdapter.virtualAttributesForSource(this.getSource());

    if (GrouperUtil.length(virtualAttributes) == 0) {
      return;
    }

    // remove the virtual attributes
    for (String virtualAttributeName : virtualAttributes.keySet()) {
      this.attributes.remove(virtualAttributeName);
      this.translationMap.remove("subject_attribute__" + virtualAttributeName.toLowerCase());
    }
  }
  
  /**
   * make sure the virtual attributes are setup for the subject
   */
  private void initVirtualAttributesLegacy() {
    
    Source source = this.getSource();
    Map virtualAttributes = BaseSourceAdapter.virtualAttributesForSourceLegacy(source);
    if (SubjectUtils.length(virtualAttributes) == 0) {
      return;
    }
    
    Map variableMap = new HashMap();
    variableMap.put("subject", this);

    Map virtualAttributeVariables = BaseSourceAdapter.virtualAttributeVariablesForSourceLegacy(source);
    if (SubjectUtils.length(virtualAttributeVariables) > 0) {
      for (String name : virtualAttributeVariables.keySet()) {
        
        String className = virtualAttributeVariables.get(name);
        Class theClass = SubjectUtils.forName(className);
        Object instance = SubjectUtils.newInstance(theClass);
        variableMap.put(name, instance);
      }
    }
    
    if (this.getTranslationMap() != null ) {
      variableMap.putAll(this.getTranslationMap());
    }
    variableMap.put("subjectUtils", new SubjectUtils());
    
    //take each attribute and init it
    for (String attributeName : virtualAttributes.keySet()) {
      
      String el = virtualAttributes.get(attributeName);

      String value =  GrouperUtil.substituteExpressionLanguage(el, variableMap, true, true, true);
      Set valueSet = new HashSet();
      valueSet.add(value);
      this.getAttributes(false).put(attributeName, valueSet);
    }
    
    
  }

  
  /**
   * @see edu.internet2.middleware.subject.Subject#getAttributes()
   */
  public Map> getAttributes() {
    Map> result = getAttributes(true);
    if (!(result instanceof SubjectCaseInsensitiveMap)) {
      log.error("Why is attribute map not case insensitive???? " + this, new RuntimeException());
    }
    return result;
  }
  
  /**
   * @see edu.internet2.middleware.subject.Subject#getAttributes(boolean)
   */
  public Map> getAttributes(boolean excludeInternalAttributes) {
    this.initAttributesIfNeeded();
    
    //note, it is assume that if not excluding that the actual
    //map will be returned so it can be modified
    if (!excludeInternalAttributes || StringUtils.isBlank(this.sourceId)) {
      return this.attributes;
    }
    
    Set internalAttributes = getSource().getInternalAttributes();
    
    if (!(internalAttributes instanceof SubjectCaseInsensitiveSet)) {
      internalAttributes = new SubjectCaseInsensitiveSetImpl(internalAttributes);
    }
    
    Map> nonInternalAttributes = new SubjectCaseInsensitiveMapImpl>();
    
    for (String attribute : attributes.keySet()) {
      if (!internalAttributes.contains(attribute)) {
        nonInternalAttributes.put(attribute, attributes.get(attribute));
      }
    }
    
    return nonInternalAttributes;
  }

  /**
   * {@inheritDoc}
   */
  public Source getSource() {
    return SourceManager.getInstance().getSource(this.sourceId);
  }

  /**
   * 
   * @param attributes1
   */
  public void setAttributes(Map> attributes1) {
    if (attributes1 == null || attributes1 instanceof SubjectCaseInsensitiveMap) {
      this.attributes = attributes1;
    } else {
      this.attributes = new SubjectCaseInsensitiveMapImpl>(attributes1);
    }
    this.attributesInitted = false;
  }


  /**
   * @see edu.internet2.middleware.subject.Subject#getTypeName()
   */
  public String getTypeName() {
    return this.typeName;
  }


  
  /**
   * @param id1 the id to set
   */
  public void setId(String id1) {
    this.id = id1;
  }


  
  /**
   * @param typeName1 the typeName to set
   */
  public void setTypeName(String typeName1) {
    this.typeName = typeName1;
  }


  /**
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public boolean equals(Object obj) {
    return equalsStatic(this, obj);
  }


  /**
   * @param subject 
   * @param obj
   * @return true if equal
   */
  public static boolean equalsStatic(Subject subject, Object obj) {
    if (subject == obj) {
      return true;
    }
    //this catches null too
    if (!(obj instanceof Subject)) {
      return false;
    }
    Subject otherObj = (Subject) obj;
    return StringUtils.equals(subject.getId(), otherObj.getId()) 
      && StringUtils.equals(subject.getSourceId(), otherObj.getSourceId());
  }


  /**
   * @see java.lang.Object#hashCode()
   */
  @Override
  public int hashCode() {
    return hashcodeStatic(this);
  }


  /**
   * @param subject 
   * @return hash code
   */
  public static int hashcodeStatic(Subject subject) {
    return new HashCodeBuilder().append(subject.getId()).append(subject.getSourceId()).toHashCode();
  }

  /**
   * @see edu.internet2.middleware.subject.Subject#getAttributeValueOrCommaSeparated(java.lang.String)
   */
  public String getAttributeValueOrCommaSeparated(String attributeName) {
    return getAttributeValueOrCommaSeparated(attributeName, true);
  }
  
  /**
   * @see edu.internet2.middleware.subject.Subject#getAttributeValueOrCommaSeparated(java.lang.String, boolean)
   */
  public String getAttributeValueOrCommaSeparated(String attributeName, boolean excludeInternalAttributes) {
    this.initAttributesIfNeeded();
    return attributeValueOrCommaSeparated(this, attributeName, excludeInternalAttributes);
  }

  /**
   * @see Subject#getAttributeValueOrCommaSeparated(String)
   * @param subject shouldnt be null
   * @param attributeName
   * @return the string
   */
  public static String attributeValueOrCommaSeparated(Subject subject, String attributeName) {
    return attributeValueOrCommaSeparated(subject, attributeName, true);
  }

  /**
   * @see Subject#getAttributeValueOrCommaSeparated(String, boolean)
   * @param subject shouldnt be null
   * @param attributeName
   * @param excludeInternalAttributes 
   * @return the string
   */
  public static String attributeValueOrCommaSeparated(Subject subject, String attributeName, boolean excludeInternalAttributes) {
    if (excludeInternalAttributes && !StringUtils.isBlank(subject.getSourceId()) && subject.getSource().getInternalAttributes().contains(attributeName)) {
      return null;
    }
    
    Set attributeValues = subject.getAttributeValues(attributeName);
    if (attributeValues == null || attributeValues.size() == 0) {
      return null;
    }
    return StringUtils.join(attributeValues.iterator(), ", ");
  }
  
  /**
   * @see edu.internet2.middleware.subject.Subject#getAttributeValueSingleValued(java.lang.String)
   */
  public String getAttributeValueSingleValued(String attributeName) {
    return getAttributeValueSingleValued(attributeName, true);
  }
  
  /**
   * @see edu.internet2.middleware.subject.Subject#getAttributeValueSingleValued(java.lang.String, boolean)
   */
  public String getAttributeValueSingleValued(String attributeName, boolean excludeInternalAttributes) {
    this.initAttributesIfNeeded();
    if (this.attributes == null) {
      return null;
    }
    
    if (excludeInternalAttributes && !StringUtils.isBlank(this.sourceId) && getSource().getInternalAttributes().contains(attributeName)) {
      return null;
    }
    
    Set values = this.attributes.get(attributeName);
    if (values != null) {
      if (values.size() > 1) {
        throw new RuntimeException(
            "This is not a single valued attribute, it is multivalued: '" + attributeName + "', " + values.size());
      }
      return values.iterator().next();
    }
    return null;
  }

  
  /**
   * @return the nameOverride
   */
  public String getNameOverride() {
    return nameOverride;
  }

  
  /**
   * @param nameOverride the nameOverride to set
   */
  public void setName(String nameOverride) {
    this.nameOverride = nameOverride;
  }

  
  /**
   * @return the descriptionOverride
   */
  public String getDescriptionOverride() {
    return descriptionOverride;
  }

  
  /**
   * @param descriptionOverride the descriptionOverride to set
   */
  public void setDescription(String descriptionOverride) {
    this.descriptionOverride = descriptionOverride;
  }

  /**
   * @return true if resolved from source (rather than cache)
   */
  public boolean isResolvedFromSource() {
    return isResolvedFromSource;
  }

  /**
   * true if resolved from source (rather than cache)
   * @param isResolvedFromSource
   */
  public void setResolvedFromSource(boolean isResolvedFromSource) {
    this.isResolvedFromSource = isResolvedFromSource;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy