/*
* Copyright (c) 2010-2016 Evolveum
*
* 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.
*/
package com.evolveum.midpoint.schema.processor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import javax.xml.namespace.QName;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType;
/**
* Resource Object.
*
* Resource Object understands resource-specific annotations, such as
* identifiers, native object class, etc.
*
* Object class can be determined by using the definition (inherited from
* PropertyContainer)
*
* @author Radovan Semancik
*
*/
public final class ResourceAttributeContainer extends PrismContainer {
private static final long serialVersionUID = 8878851067509560312L;
/**
* The constructors should be used only occasionally (if used at all).
* Use the factory methods in the ResourceObjectDefintion instead.
*/
public ResourceAttributeContainer(QName name, ResourceAttributeContainerDefinition definition, PrismContext prismContext) {
super(name, definition, prismContext);
}
@Override
public ResourceAttributeContainerDefinition getDefinition() {
PrismContainerDefinition prismContainerDefinition = super.getDefinition();
if (prismContainerDefinition == null) {
return null;
}
if (prismContainerDefinition instanceof ResourceAttributeContainerDefinition) {
return (ResourceAttributeContainerDefinition) prismContainerDefinition;
} else {
throw new IllegalStateException("definition should be " + ResourceAttributeContainerDefinition.class + " but it is " + prismContainerDefinition.getClass() + " instead; definition = " + prismContainerDefinition.debugDump(0));
}
}
/**
* Returns set of resource object attributes.
*
* The order of attributes is insignificant.
*
* The returned set is imutable! Any change to it will be ignored.
*
* @return set of resource object attributes.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public Collection<ResourceAttribute<?>> getAttributes() {
// TODO: Iterate over the list to assert correct types
return (Set) getValue().getProperties();
}
public void add(ResourceAttribute<?> attribute) throws SchemaException {
super.add(attribute);
}
/**
* Returns a (single) primary identifier.
*
* This method returns a property that acts as an (primary) identifier for
* the resource object. Primary identifiers are used to access the resource
* objects, retrieve them from resource, identify objects for modifications,
* etc.
*
* Returns null if no identifier is defined.
*
* Resource objects may have multiple (composite) identifiers, but this
* method assumes that there is only a single identifier. The method will
* throw exception if that assumption is not satisfied.
*
* @return identifier property
* @throws IllegalStateException
* if resource object has multiple identifiers
*/
public PrismProperty<?> getPrimaryIdentifier() {
Collection<ResourceAttribute<?>> attrDefs = getPrimaryIdentifiers();
if (attrDefs.size() > 1){
throw new IllegalStateException("Resource object has more than one identifier.");
}
for (PrismProperty<?> p : attrDefs){
return p;
}
return null;
}
/**
* Returns primary identifiers.
*
* This method returns properties that act as (primary) identifiers for the
* resource object. Primary identifiers are used to access the resource
* objects, retrieve them from resource, identify objects for modifications,
* etc.
*
* Returns empty set if no identifier is defined. Must not return null.
*
* Resource objects may have multiple (composite) identifiers, all of them
* are returned.
*
* The returned set it immutable! Any modifications will be lost.
*
* @return set of identifier properties
*/
public Collection<ResourceAttribute<?>> getPrimaryIdentifiers() {
return extractAttributesByDefinitions(getDefinition().getPrimaryIdentifiers());
}
/**
* Returns a (single) secondary identifier.
*
* This method returns a property that acts as an secondary identifier for
* the resource object. Secondary identifiers are used to confirm primary
* identification of resource object.
*
* Returns null if no secondary identifier is defined.
*
* Resource objects may have multiple (composite) identifiers, but this
* method assumes that there is only a single identifier. The method will
* throw exception if that assumption is not satisfied.
*
* @return secondary identifier property
* @throws IllegalStateException
* if resource object has multiple secondary identifiers
*/
public PrismProperty getSecondaryIdentifier() {
Collection<ResourceAttribute<?>> secondaryIdentifiers = getSecondaryIdentifiers();
if (secondaryIdentifiers.size() > 1){
throw new IllegalStateException("Resource object has more than one identifier.");
}
for (PrismProperty<?> p : secondaryIdentifiers){
return p;
}
return null;
}
/**
* Returns secondary identifiers.
*
* This method returns properties that act as secondary identifiers for the
* resource object. Secondary identifiers are used to confirm primary
* identification of resource object.
*
* Returns empty set if no identifier is defined. Must not return null.
*
* Resource objects may have multiple (composite) identifiers, all of them
* are returned.
*
* @return set of secondary identifier properties
*/
public Collection<ResourceAttribute<?>> getSecondaryIdentifiers() {
return extractAttributesByDefinitions(getDefinition().getSecondaryIdentifiers());
}
public Collection<ResourceAttribute<?>> getAllIdentifiers() {
return extractAttributesByDefinitions(getDefinition().getAllIdentifiers());
}
private Collection<ResourceAttribute<?>> extractAttributesByDefinitions(Collection<? extends ResourceAttributeDefinition> definitions) {
Collection<ResourceAttribute<?>> attributes = new ArrayList<ResourceAttribute<?>>(definitions.size());
for (ResourceAttributeDefinition attrDef : definitions) {
for (ResourceAttribute<?> property : getAttributes()){
if (attrDef.getName().equals(property.getElementName())){
property.setDefinition(attrDef);
attributes.add(property);
}
}
}
return attributes;
}
/**
* Returns description attribute of a resource object.
*
* Returns null if there is no description attribute or the attribute is not
* known.
*
* The exception should be never thrown unless there is some bug in the
* code. The validation of model consistency should be done at the time of
* schema parsing.
*
* @return description attribute of a resource object.
* @throws IllegalStateException
* if there is no definition for the referenced attributed
*/
public ResourceAttribute getDescriptionAttribute() {
if (getDefinition() == null) {
return null;
}
return findAttribute(getDefinition().getDisplayNameAttribute());
}
/**
* Specifies which resource attribute should be used as a "technical" name
* for the account. This name will appear in log files and other troubleshooting
* tools. The name should be a form of unique identifier that can be used to
* locate the resource object for diagnostics. It should not contain white chars and
* special chars if that can be avoided and it should be reasonable short.
* It is different from a display name attribute. Display name is intended for a
* common user or non-technical administrator (such as role administrator). The
* naming attribute is intended for technical IDM administrators and developers.
*
* @return attribute that should be used as a "technical" name
* for the account.
*/
public ResourceAttribute<String> getNamingAttribute() {
if (getDefinition() == null) {
return null;
}
if (getDefinition().getNamingAttribute()==null) {
return null;
}
return findAttribute(getDefinition().getNamingAttribute());
}
/**
* Returns display name attribute of a resource object.
*
* Returns null if there is no display name attribute or the attribute is
* not known.
*
* The exception should be never thrown unless there is some bug in the
* code. The validation of model consistency should be done at the time of
* schema parsing.
*
* @return display name attribute of a resource object.
* @throws IllegalStateException
* if there is no definition for the referenced attributed
*/
public ResourceAttribute getDisplayNameAttribute() {
if (getDefinition() == null) {
return null;
}
return findAttribute(getDefinition().getDisplayNameAttribute());
}
/**
* Returns the native object class string for the resource object.
*
* Native object class is the name of the Resource Object Definition (Object
* Class) as it is seen by the resource itself. The name of the Resource
* Object Definition may be constrained by XSD or other syntax and therefore
* may be "mangled" to conform to such syntax. The <i>native object
* class</i> value will contain unmangled name (if available).
*
* Returns null if there is no native object class or the native object
* class is not known.
*
* The exception should be never thrown unless there is some bug in the
* code. The validation of model consistency should be done at the time of
* schema parsing.
*
* @return native object class
* @throws IllegalStateException
* if there is more than one description attribute.
*/
public String getNativeObjectClass() {
return getDefinition() == null ? null : getDefinition().getNativeObjectClass();
}
public ShadowKindType getKind() {
ResourceAttributeContainerDefinition definition = getDefinition();
return (definition != null ? definition.getKind() : null);
}
/**
* Indicates whether definition is should be used as default account type.
*
* If true value is returned then the definition should be used as a default
* account type definition. This is a way how a resource connector may
* suggest applicable object classes (resource object definitions) for
* accounts.
*
* If no information about account type is present, false should be
* returned. This method must return true only if isAccountType() returns
* true.
*
* The exception should be never thrown unless there is some bug in the
* code. The validation of at-most-one value should be done at the time of
* schema parsing. The exception may not even be thrown at all if the
* implementation is not able to determine duplicity.
*
* @return true if the definition should be used as account type.
* @throws IllegalStateException
* if more than one default account is suggested in the schema.
*/
public boolean isDefaultInAKind() {
ResourceAttributeContainerDefinition definition = getDefinition();
return (definition != null ? definition.isDefaultInAKind() : null);
}
/**
* Finds a specific attribute in the resource object by name.
*
* Returns null if nothing is found.
*
* @param attributeQName
* attribute name to find.
* @return found attribute or null
*/
@SuppressWarnings("unchecked")
public <X> ResourceAttribute<X> findAttribute(QName attributeQName) {
return (ResourceAttribute<X>) super.findProperty(attributeQName);
}
/**
* Finds a specific attribute in the resource object by definition.
*
* Returns null if nothing is found.
*
* @param attributeDefinition
* attribute definition to find.
* @return found attribute or null
*/
public <X> ResourceAttribute<X> findAttribute(ResourceAttributeDefinition attributeDefinition) {
return (ResourceAttribute<X>) getValue().findProperty(attributeDefinition);
}
public <X> ResourceAttribute<X> findOrCreateAttribute(ResourceAttributeDefinition attributeDefinition) throws SchemaException {
return (ResourceAttribute<X>) getValue().findOrCreateProperty(attributeDefinition);
}
public <X> ResourceAttribute<X> findOrCreateAttribute(QName attributeName) throws SchemaException {
return (ResourceAttribute<X>) getValue().findOrCreateProperty(attributeName);
}
public <T> boolean contains(ResourceAttribute<T> attr) {
return getValue().contains(attr);
}
public static ResourceAttributeContainer convertFromContainer(PrismContainer<?> origAttrContainer,
ObjectClassComplexTypeDefinition objectClassDefinition) throws SchemaException {
if (origAttrContainer == null) {
return null;
}
QName elementName = origAttrContainer.getElementName();
ResourceAttributeContainer attributesContainer = createEmptyContainer(elementName, objectClassDefinition);
for (Item item: origAttrContainer.getValue().getItems()) {
if (item instanceof PrismProperty) {
PrismProperty<?> property = (PrismProperty)item;
QName attributeName = property.getElementName();
ResourceAttributeDefinition attributeDefinition = objectClassDefinition.findAttributeDefinition(attributeName);
if (attributeDefinition == null) {
throw new SchemaException("No definition for attribute "+attributeName+" in object class "+objectClassDefinition);
}
ResourceAttribute attribute = new ResourceAttribute(attributeName, attributeDefinition , property.getPrismContext());
for(PrismPropertyValue pval: property.getValues()) {
attribute.add(pval.clone());
}
attributesContainer.add(attribute);
attribute.applyDefinition(attributeDefinition);
} else {
throw new SchemaException("Cannot process item of type "+item.getClass().getSimpleName()+", attributes can only be properties");
}
}
return attributesContainer;
}
public static ResourceAttributeContainer createEmptyContainer(QName elementName, ObjectClassComplexTypeDefinition objectClassDefinition) {
ResourceAttributeContainerDefinition attributesContainerDefinition = new ResourceAttributeContainerDefinitionImpl(elementName,
objectClassDefinition, objectClassDefinition.getPrismContext());
return new ResourceAttributeContainer(elementName, attributesContainerDefinition , objectClassDefinition.getPrismContext());
}
@Override
public ResourceAttributeContainer clone() {
ResourceAttributeContainer clone = new ResourceAttributeContainer(getElementName(), getDefinition(), getPrismContext());
copyValues(clone);
return clone;
}
protected void copyValues(ResourceAttributeContainer clone) {
super.copyValues(clone);
// Nothing to copy
}
@Override
public void checkConsistenceInternal(Itemable rootItem, boolean requireDefinitions, boolean prohibitRaw,
ConsistencyCheckScope scope) {
super.checkConsistenceInternal(rootItem, requireDefinitions, prohibitRaw, scope);
List<PrismContainerValue> values = getValues();
if (values == null) {
throw new IllegalStateException("Null values in ResourceAttributeContainer");
}
if (values.isEmpty()) {
return;
}
if (values.size() > 1) {
throw new IllegalStateException(values.size()+" values in ResourceAttributeContainer, expected just one");
}
PrismContainerValue value = values.get(0);
List<Item<?,?>> items = value.getItems();
if (items == null) {
return;
}
for (Item item: items) {
if (!(item instanceof ResourceAttribute)) {
throw new IllegalStateException("Found illegal item in ResourceAttributeContainer: "+item+" ("+item.getClass()+")");
}
}
}
/**
* Return a human readable name of this class suitable for logs.
*/
protected String getDebugDumpClassName() {
return "RAC";
}
}