/* * 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.web.component.prism; import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; import com.evolveum.midpoint.gui.api.util.ModelServiceLocator; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.path.ItemPathSegment; import com.evolveum.midpoint.prism.path.NameItemPathSegment; import com.evolveum.midpoint.prism.schema.PrismSchema; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ConnectorTypeUtil; import com.evolveum.midpoint.schema.util.ReportTypeUtil; import com.evolveum.midpoint.schema.util.ResourceTypeUtil; import com.evolveum.midpoint.util.exception.ConfigurationException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ActivationCapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CredentialsCapabilityType; import org.apache.commons.lang.Validate; import javax.xml.namespace.QName; import java.util.*; /** * @author Viliam Repan (lazyman) */ public class ObjectWrapperFactory { private static final Trace LOGGER = TraceManager.getTrace(ObjectWrapperFactory.class); private static final String DOT_CLASS = ObjectWrapperFactory.class.getName() + "."; private static final String CREATE_CONTAINERS = DOT_CLASS + "createContainers"; private static final String CREATE_OBJECT_WRAPPER = DOT_CLASS + "createObjectWrapper"; private static final List<QName> INHERITED_OBJECT_SUBCONTAINERS = Arrays.asList( ObjectType.F_METADATA, ObjectType.F_EXTENSION); private static final List<QName> CONTAINERS_TO_IGNORE = Arrays.asList( SubjectedObjectSelectorType.COMPLEX_TYPE, TriggerType.COMPLEX_TYPE, ApprovalSchemaType.COMPLEX_TYPE, PasswordHistoryEntryType.COMPLEX_TYPE, NonceType.COMPLEX_TYPE); private ModelServiceLocator modelServiceLocator; private OperationResult result; public ObjectWrapperFactory(ModelServiceLocator modelServiceLocator) { Validate.notNull(modelServiceLocator, "Service locator must not be null"); this.modelServiceLocator = modelServiceLocator; } public OperationResult getResult() { return result; } public <O extends ObjectType> ObjectWrapper<O> createObjectWrapper(String displayName, String description, PrismObject<O> object, ContainerStatus status) { return createObjectWrapper(displayName, description, object, status, false, AuthorizationPhaseType.REQUEST); } public <O extends ObjectType> ObjectWrapper<O> createObjectWrapper(String displayName, String description, PrismObject<O> object, ContainerStatus status, boolean delayContainerCreation, AuthorizationPhaseType authorizationPhase) { if (authorizationPhase == null) { authorizationPhase = AuthorizationPhaseType.REQUEST; } try { OperationResult result = new OperationResult(CREATE_OBJECT_WRAPPER); PrismObjectDefinition<O> objectDefinitionForEditing = modelServiceLocator.getModelInteractionService() .getEditObjectDefinition(object, authorizationPhase, result); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Edit definition for {}:\n{}", object, objectDefinitionForEditing.debugDump(1)); } RefinedObjectClassDefinition objectClassDefinitionForEditing = null; if (isShadow(object)) { PrismReference resourceRef = object.findReference(ShadowType.F_RESOURCE_REF); PrismObject<ResourceType> resource = resourceRef.getValue().getObject(); Validate.notNull(resource, "No resource object in the resourceRef"); objectClassDefinitionForEditing = modelServiceLocator.getModelInteractionService().getEditObjectClassDefinition( (PrismObject<ShadowType>) object, resource, authorizationPhase); } return createObjectWrapper(displayName, description, object, objectDefinitionForEditing, objectClassDefinitionForEditing, status, delayContainerCreation, result); } catch (SchemaException | ConfigurationException | ObjectNotFoundException ex) { throw new SystemException(ex); } } public <O extends ObjectType> ObjectWrapper<O> createObjectWrapper(String displayName, String description, PrismObject<O> object, PrismObjectDefinition<O> objectDefinitionForEditing, RefinedObjectClassDefinition objectClassDefinitionForEditing, ContainerStatus status, boolean delayContainerCreation) { return createObjectWrapper(displayName, description, object, objectDefinitionForEditing, objectClassDefinitionForEditing, status, delayContainerCreation, null); } private <O extends ObjectType> ObjectWrapper<O> createObjectWrapper(String displayName, String description, PrismObject<O> object, PrismObjectDefinition<O> objectDefinitionForEditing, RefinedObjectClassDefinition objectClassDefinitionForEditing, ContainerStatus status, boolean delayContainerCreation, OperationResult result) { if (result == null) { this.result = new OperationResult(CREATE_OBJECT_WRAPPER); } else { this.result = result; } ObjectWrapper<O> objectWrapper = new ObjectWrapper<O>(displayName, description, object, objectDefinitionForEditing, objectClassDefinitionForEditing, status, delayContainerCreation); List<ContainerWrapper<? extends Containerable>> containerWrappers = createContainerWrappers(objectWrapper, object, objectDefinitionForEditing, status, this.result); objectWrapper.setContainers(containerWrappers); this.result.computeStatusIfUnknown(); objectWrapper.setResult(this.result); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Created object wrapper:\n{}", objectWrapper.debugDump()); } return objectWrapper; } private <O extends ObjectType> List<ContainerWrapper<? extends Containerable>> createContainerWrappers(ObjectWrapper<O> oWrapper, PrismObject<O> object, PrismObjectDefinition<O> objectDefinitionForEditing, ContainerStatus cStatus, OperationResult pResult) { OperationResult result = pResult.createSubresult(CREATE_CONTAINERS); List<ContainerWrapper<? extends Containerable>> containerWrappers = new ArrayList<>(); ContainerWrapperFactory cwf = new ContainerWrapperFactory(modelServiceLocator); try { Class<O> clazz = object.getCompileTimeClass(); if (ShadowType.class.isAssignableFrom(clazz)) { addShadowContainers(containerWrappers, oWrapper, object, objectDefinitionForEditing, cwf, cStatus, result); } else if (ResourceType.class.isAssignableFrom(clazz)) { addResourceContainers(containerWrappers, oWrapper, object, result); } else if (ReportType.class.isAssignableFrom(clazz)) { addReportContainers(containerWrappers, oWrapper, object, result); } else { ContainerWrapper mainContainerWrapper = cwf.createContainerWrapper(oWrapper, object, cStatus, null); mainContainerWrapper.setDisplayName("prismContainer.mainPanelDisplayName"); result.addSubresult(cwf.getResult()); containerWrappers.add(mainContainerWrapper); addContainerWrappers(containerWrappers, oWrapper, object, null, result); } } catch (SchemaException | RuntimeException e) { //TODO: shouldn't be this exception thrown???? LoggingUtils.logUnexpectedException(LOGGER, "Error occurred during container wrapping", e); result.recordFatalError("Error occurred during container wrapping, reason: " + e.getMessage(), e); } Collections.sort(containerWrappers, new ItemWrapperComparator()); result.recomputeStatus(); result.recordSuccessIfUnknown(); return containerWrappers; } private <O extends ObjectType> PrismObjectDefinition<O> getDefinition(PrismObject<O> object, PrismObjectDefinition<O> objectDefinitionForEditing) { if (objectDefinitionForEditing != null) { return objectDefinitionForEditing; } return object.getDefinition(); } private <O extends ObjectType> List<ContainerWrapper<? extends Containerable>> createCustomContainerWrapper( ObjectWrapper<O> oWrapper, PrismObject<O> object, PrismObjectDefinition<O> objectDefinitionForEditing, QName name, OperationResult result) throws SchemaException { PrismContainer container = object.findContainer(name); ContainerStatus status = container == null ? ContainerStatus.ADDING : ContainerStatus.MODIFYING; List<ContainerWrapper<? extends Containerable>> list = new ArrayList<>(); if (container == null) { PrismContainerDefinition<?> definition = getDefinition(object, objectDefinitionForEditing).findContainerDefinition(name); container = definition.instantiate(); } ContainerWrapperFactory cwf = new ContainerWrapperFactory(modelServiceLocator); ContainerWrapper wrapper = cwf.createContainerWrapper(oWrapper, container, status, new ItemPath(name)); result.addSubresult(cwf.getResult()); list.add(wrapper); if (!ShadowType.F_ASSOCIATION.equals(name)) { addContainerWrappers(list, oWrapper, container, new ItemPath(name), result); } return list; } private <O extends ObjectType, C extends Containerable> void addContainerWrappers( List<ContainerWrapper<? extends Containerable>> containerWrappers, ObjectWrapper<O> oWrapper, PrismContainer<C> parentContainer, ItemPath path, OperationResult result) throws SchemaException { PrismContainerDefinition<C> parentContainerDefinition = parentContainer.getDefinition(); List<ItemPathSegment> segments = new ArrayList<>(); if (path != null) { segments.addAll(path.getSegments()); } ItemPath parentPath = new ItemPath(segments); for (ItemDefinition def : (Collection<ItemDefinition>) parentContainerDefinition.getDefinitions()) { if (!(def instanceof PrismContainerDefinition)) { continue; } if (isIgnoreContainer(def.getTypeName())) { continue; } LOGGER.trace("ObjectWrapper.createContainerWrapper processing definition: {}", def); PrismContainerDefinition<?> containerDef = (PrismContainerDefinition) def; //todo this oWrapper.isShowAssignments() value is not set when initialization occurs (only default is there) [lazyman] if (!oWrapper.isShowAssignments() && AssignmentType.COMPLEX_TYPE.equals(containerDef.getTypeName())) { continue; } //todo this oWrapper.isShowInheritedObjectAttributes() value is not set when initialization occurs (only default is there) [lazyman] if (!oWrapper.isShowInheritedObjectAttributes()) { boolean res = INHERITED_OBJECT_SUBCONTAINERS.contains(containerDef.getName()); LOGGER.info("checking " + containerDef.getName() + ", result = " + res); if (res) { continue; } } ItemPath newPath = createPropertyPath(parentPath, containerDef.getName()); // [med] // The following code fails to work when parent is multivalued or // potentially multivalued. // Therefore (as a brutal hack), for multivalued parents we simply // skip it. if (parentContainer.size() <= 1) { // the same check as in getValue() implementation boolean isMultiValued = parentContainer.getDefinition() != null && !parentContainer.getDefinition().isDynamic() && !parentContainer.getDefinition().isSingleValue(); if (!isMultiValued) { ContainerWrapperFactory cwf = new ContainerWrapperFactory(modelServiceLocator); PrismContainer prismContainer = parentContainer.findContainer(def.getName()); ContainerWrapper container; if (prismContainer != null) { container = cwf.createContainerWrapper(oWrapper, prismContainer, ContainerStatus.MODIFYING, newPath); } else { prismContainer = containerDef.instantiate(); container = cwf.createContainerWrapper(oWrapper, prismContainer, ContainerStatus.ADDING, newPath); } result.addSubresult(cwf.getResult()); containerWrappers.add(container); if (!AssignmentType.COMPLEX_TYPE.equals(containerDef.getTypeName()) || !ShadowType.F_ASSOCIATION.equals(parentContainer.getElementName())) { // do not show internals of Assignments (e.g. activation) addContainerWrappers(containerWrappers, oWrapper, prismContainer, newPath, result); } } } } } private boolean isIgnoreContainer(QName containerDefinitionName) { for (QName container : CONTAINERS_TO_IGNORE) { if (container.equals(containerDefinitionName)){ return true; } } return false; } private boolean hasResourceCapability(ResourceType resource, Class<? extends CapabilityType> capabilityClass) { if (resource == null) { return false; } return ResourceTypeUtil.hasEffectiveCapability(resource, capabilityClass); } private <O extends ObjectType> void addResourceContainerWrapper( List<ContainerWrapper<? extends Containerable>> containerWrappers, ObjectWrapper<O> oWrapper, PrismObject<O> object, PrismObject<ConnectorType> connector, OperationResult result) throws SchemaException { PrismContainer<ConnectorConfigurationType> container = object.findContainer(ResourceType.F_CONNECTOR_CONFIGURATION); ConnectorType connectorType = connector.asObjectable(); PrismSchema schema = ConnectorTypeUtil.parseConnectorSchema(connectorType, connector.getPrismContext()); PrismContainerDefinition<ConnectorConfigurationType> definition = ConnectorTypeUtil.findConfigurationContainerDefinition( connectorType, schema); // brutal hack - the definition has (errorneously) set maxOccurs = // unbounded. But there can be only one configuration container. // See MID-2317 and related issues PrismContainerDefinition<ConnectorConfigurationType> definitionFixed = definition.clone(); ((PrismContainerDefinitionImpl) definitionFixed).setMaxOccurs(1); if (container == null) { container = definitionFixed.instantiate(); } else { container.setDefinition(definitionFixed); } addContainerWrappers(containerWrappers, oWrapper, container, new ItemPath(ResourceType.F_CONNECTOR_CONFIGURATION), result); } private <O extends ObjectType> void addShadowContainers( List<ContainerWrapper<? extends Containerable>> containers, ObjectWrapper<O> oWrapper, PrismObject<O> object, PrismObjectDefinition<O> objectDefinitionForEditing, ContainerWrapperFactory cwf, ContainerStatus cStatus, OperationResult result) throws SchemaException { PrismContainer attributesContainer = object.findContainer(ShadowType.F_ATTRIBUTES); ContainerStatus status = attributesContainer != null ? cStatus : ContainerStatus.ADDING; if (attributesContainer == null) { PrismContainerDefinition<?> definition = object.getDefinition().findContainerDefinition( ShadowType.F_ATTRIBUTES); attributesContainer = definition.instantiate(); } ContainerWrapper attributesContainerWrapper = cwf.createContainerWrapper(oWrapper, attributesContainer, status, new ItemPath(ShadowType.F_ATTRIBUTES)); result.addSubresult(cwf.getResult()); attributesContainerWrapper.setMain(true); attributesContainerWrapper.setDisplayName("prismContainer.shadow.mainPanelDisplayName"); containers.add(attributesContainerWrapper); if (hasResourceCapability(((ShadowType) object.asObjectable()).getResource(), ActivationCapabilityType.class)) { containers .addAll(createCustomContainerWrapper(oWrapper, object, objectDefinitionForEditing, ShadowType.F_ACTIVATION, result)); } if (hasResourceCapability(((ShadowType) object.asObjectable()).getResource(), CredentialsCapabilityType.class)) { containers .addAll(createCustomContainerWrapper(oWrapper, object, objectDefinitionForEditing, ShadowType.F_CREDENTIALS, result)); } PrismContainer<ShadowAssociationType> associationContainer = object.findOrCreateContainer(ShadowType.F_ASSOCIATION); attributesContainerWrapper = cwf.createContainerWrapper(oWrapper, associationContainer, ContainerStatus.MODIFYING, new ItemPath(ShadowType.F_ASSOCIATION)); result.addSubresult(cwf.getResult()); containers.add(attributesContainerWrapper); } private <O extends ObjectType> void addResourceContainers( List<ContainerWrapper<? extends Containerable>> containers, ObjectWrapper<O> oWrapper, PrismObject<O> object, OperationResult result) throws SchemaException { PrismObject<ConnectorType> connector = loadConnector(object); if (connector != null) { addResourceContainerWrapper(containers, oWrapper, object, connector, result); } } private <O extends ObjectType> void addReportContainers( List<ContainerWrapper<? extends Containerable>> containers, ObjectWrapper<O> oWrapper, PrismObject<O> object, OperationResult result) throws SchemaException { PrismContainer container = object.findContainer(ReportType.F_CONFIGURATION); ContainerStatus status = container != null ? ContainerStatus.MODIFYING : ContainerStatus.ADDING; if (container == null) { PrismSchema schema = ReportTypeUtil.parseReportConfigurationSchema( (PrismObject<ReportType>) object, object.getPrismContext()); PrismContainerDefinition<?> definition = ReportTypeUtil.findReportConfigurationDefinition(schema); if (definition == null) { return; } container = definition.instantiate(); } ContainerWrapperFactory cwf = new ContainerWrapperFactory(modelServiceLocator); ContainerWrapper wrapper = cwf.createContainerWrapper(oWrapper, container, status, new ItemPath(ReportType.F_CONFIGURATION)); result.addSubresult(cwf.getResult()); containers.add(wrapper); } private PrismObject<ConnectorType> loadConnector(PrismObject object) { PrismReference connectorRef = object.findReference(ResourceType.F_CONNECTOR_REF); return connectorRef != null ? (connectorRef.getValue() != null ? connectorRef.getValue().getObject() : null) : null; // todo reimplement } private ItemPath createPropertyPath(ItemPath path, QName element) { List<ItemPathSegment> segments = new ArrayList<>(); segments.addAll(path.getSegments()); segments.add(new NameItemPathSegment(element)); return new ItemPath(segments); } private boolean isShadow(PrismObject object) { return (object.getCompileTimeClass() != null && ShadowType.class.isAssignableFrom(object .getCompileTimeClass())) || (object.getDefinition() != null && object.getDefinition().getName() .equals(ShadowType.COMPLEX_TYPE)); } }