/** @author semancik */
public class ReflectionXPathFunctionResolver implements XPathFunctionResolver {

  private static final Object LOG_FUNCTION_NAME = "logDebug";

  public static final Trace LOGGER = TraceManager.getTrace(ReflectionXPathFunctionResolver.class);

  private Collection<FunctionLibrary> functions;

  public ReflectionXPathFunctionResolver(Collection<FunctionLibrary> functions) {
    super();
    this.functions = functions;
  }

  /* (non-Javadoc)
   * @see javax.xml.xpath.XPathFunctionResolver#resolveFunction(javax.xml.namespace.QName, int)
   */
  @Override
  public XPathFunction resolveFunction(QName functionQName, int arity) {
    boolean enableDebug = false;
    String namespace = functionQName.getNamespaceURI();
    if (StringUtils.isEmpty(namespace)) {
      namespace = MidPointConstants.NS_FUNC_BASIC;
      enableDebug = true;
    } else if (namespace.equals(MidPointConstants.NS_FUNC_BASIC)) {
      enableDebug = true;
    }

    FunctionLibrary lib = findLibrary(namespace);
    if (lib == null) {
      LOGGER.trace(
          "Unknown namespace for function {} function with {} arguments", functionQName, arity);
      return null;
    }

    Object functionObject = null;
    if (lib.getXmlFunctions() != null) {
      functionObject = lib.getXmlFunctions();
    } else {
      functionObject = lib.getGenericFunctions();
    }

    String functionName = functionQName.getLocalPart();

    LOGGER.trace("Resolving to {} function with {} arguments", functionName, arity);
    ReflectionXPathFunctionWrapper xPathFunction =
        new ReflectionXPathFunctionWrapper(functionObject, functionName, arity, enableDebug);
    return xPathFunction;
  }

  private FunctionLibrary findLibrary(String namespace) {
    for (FunctionLibrary lib : functions) {
      if (lib.getNamespace().equals(namespace)) {
        return lib;
      }
    }
    return null;
  }
}
/** @author mederly */
public class MidPointTaskListener implements TaskListener {

  private static final Trace LOGGER = TraceManager.getTrace(MidPointTaskListener.class);
  private static final String DOT_CLASS = MidPointTaskListener.class.getName() + ".";

  @Override
  public void notify(DelegateTask delegateTask) {

    if (LOGGER.isTraceEnabled()) {
      LOGGER.trace(
          "notify called; event name = {}, name = {}",
          delegateTask.getEventName(),
          delegateTask.getName());
    }

    ActivitiInterface activitiInterface = SpringApplicationContextHolder.getActivitiInterface();
    activitiInterface.notifyMidpointAboutTaskEvent(delegateTask);
  }
}
/**
 * Generic change aspect for adding a resource assignment to (any) focus type.
 *
 * @author mederly
 */
@Component
public abstract class AddResourceAssignmentAspect<F extends FocusType>
    extends AddAssignmentAspect<ResourceType, F> {

  private static final Trace LOGGER = TraceManager.getTrace(AddResourceAssignmentAspect.class);

  @Autowired protected ResourceAssignmentHelper specificAssignmentHelper;

  @Override
  public boolean isEnabledByDefault() {
    return true;
  }

  @Override
  protected boolean isAssignmentRelevant(AssignmentType assignmentType) {
    return specificAssignmentHelper.isResourceAssignment(assignmentType);
  }

  @Override
  protected boolean shouldAssignmentBeApproved(
      PcpAspectConfigurationType config, ResourceType resourceType) {
    return specificAssignmentHelper.shouldAssignmentBeApproved(config, resourceType);
  }

  @Override
  protected ApprovalRequest<AssignmentType> createApprovalRequest(
      PcpAspectConfigurationType config, AssignmentType assignmentType, ResourceType resourceType) {
    return specificAssignmentHelper.createApprovalRequest(config, assignmentType, resourceType);
  }

  @Override
  protected ResourceType getAssignmentApprovalTarget(
      AssignmentType assignmentType, OperationResult result) {
    return specificAssignmentHelper.getAssignmentApprovalTarget(assignmentType, result);
  }

  @Override
  protected AssignmentType cloneAndCanonicalizeAssignment(AssignmentType assignmentType) {
    return specificAssignmentHelper.cloneAndCanonicalizeAssignment(assignmentType);
  }
}
@Component
public class PasswordPolicyProcessor {

  private static final Trace LOGGER = TraceManager.getTrace(PasswordPolicyProcessor.class);

  @Autowired(required = true)
  Protector protector;

  @Autowired(required = true)
  ModelObjectResolver resolver;

  void processPasswordPolicy(
      ValuePolicyType passwordPolicy, PrismProperty password, OperationResult result)
      throws PolicyViolationException, SchemaException {

    if (passwordPolicy == null) {
      LOGGER.trace("Skipping processing password policies. Password policy not specified.");
      return;
    }

    String passwordValue = determinePasswordValue(password);

    boolean isValid = PasswordPolicyUtils.validatePassword(passwordValue, passwordPolicy, result);

    if (!isValid) {
      result.computeStatus();
      throw new PolicyViolationException(
          "Provided password does not satisfy password policies. " + result.getMessage());
    }
  }

  <F extends FocusType> void processPasswordPolicy(
      LensFocusContext<F> focusContext, LensContext<F> context, Task task, OperationResult result)
      throws PolicyViolationException, SchemaException {

    if (!UserType.class.isAssignableFrom(focusContext.getObjectTypeClass())) {
      LOGGER.trace("Skipping processing password policies because focus is not user");
      return;
    }

    //		PrismProperty<PasswordType> password = getPassword(focusContext);
    ObjectDelta userDelta = focusContext.getDelta();

    if (userDelta == null) {
      LOGGER.trace("Skipping processing password policies. User delta not specified.");
      return;
    }

    if (userDelta.isDelete()) {
      LOGGER.trace("Skipping processing password policies. User will be deleted.");
      return;
    }

    PrismProperty<PasswordType> password = null;
    PrismObject<F> user;
    if (ChangeType.ADD == userDelta.getChangeType()) {
      user = focusContext.getDelta().getObjectToAdd();
      if (user != null) {
        password = user.findProperty(SchemaConstants.PATH_PASSWORD_VALUE);
      }
      if (password == null) {
        if (wasExecuted(userDelta, focusContext)) {
          LOGGER.trace(
              "Skipping processing password policies. User addition was already executed.");
          return;
        }
      }
    } else if (ChangeType.MODIFY == userDelta.getChangeType()) {
      PropertyDelta<PasswordType> passwordValueDelta;
      if (userDelta != null) {
        passwordValueDelta = userDelta.findPropertyDelta(SchemaConstants.PATH_PASSWORD_VALUE);
        if (passwordValueDelta == null) {
          LOGGER.trace(
              "Skipping processing password policies. User delta does not contain password change.");
          return;
        }
        if (userDelta.getChangeType() == ChangeType.MODIFY && passwordValueDelta != null) {
          if (passwordValueDelta.isAdd()) {
            password =
                (PrismProperty<PasswordType>) passwordValueDelta.getItemNewMatchingPath(null);
          } else if (passwordValueDelta.isDelete()) {
            password = null;
          } else {
            password =
                (PrismProperty<PasswordType>) passwordValueDelta.getItemNewMatchingPath(null);
          }
        } else {
          password = (PrismProperty<PasswordType>) passwordValueDelta.getItemNewMatchingPath(null);
        }
      }
    }

    ValuePolicyType passwordPolicy;
    if (focusContext.getOrgPasswordPolicy() == null) {
      passwordPolicy =
          determineValuePolicy(userDelta, focusContext.getObjectAny(), context, task, result);
      focusContext.setOrgPasswordPolicy(passwordPolicy);
    } else {
      passwordPolicy = focusContext.getOrgPasswordPolicy();
    }

    processPasswordPolicy(passwordPolicy, password, result);
  }

  private <F extends FocusType> boolean wasExecuted(
      ObjectDelta<UserType> userDelta, LensFocusContext<F> focusContext) {

    for (LensObjectDeltaOperation<F> executedDeltaOperation : focusContext.getExecutedDeltas()) {
      ObjectDelta<F> executedDelta = executedDeltaOperation.getObjectDelta();
      if (!executedDelta.isAdd()) {
        continue;
      } else if (executedDelta.getObjectToAdd() != null
          && executedDelta.getObjectTypeClass().equals(UserType.class)) {
        return true;
      }
    }

    return false;
  }

  // TODO: maybe some caching of orgs?????
  private <T extends ObjectType, F extends ObjectType> ValuePolicyType determineValuePolicy(
      ObjectDelta<UserType> userDelta,
      PrismObject<T> object,
      LensContext<F> context,
      Task task,
      OperationResult result)
      throws SchemaException {
    // check the modification of organization first
    ValuePolicyType valuePolicy = determineValuePolicy(userDelta, task, result);

    // if null, check the existing organization
    if (valuePolicy == null) {
      valuePolicy = determineValuePolicy(object, task, result);
    }

    // if still null, just use global policy
    if (valuePolicy == null) {
      valuePolicy = context.getEffectivePasswordPolicy();
    }

    if (valuePolicy != null) {
      LOGGER.trace(
          "Value policy {} will be user to check password.", valuePolicy.getName().getOrig());
    }

    return valuePolicy;
  }

  private ValuePolicyType determineValuePolicy(
      ObjectDelta<UserType> userDelta, Task task, OperationResult result) throws SchemaException {
    ReferenceDelta orgDelta = userDelta.findReferenceModification(UserType.F_PARENT_ORG_REF);
    ValuePolicyType passwordPolicy = null;
    LOGGER.trace("Determining password policy from org delta.");
    if (orgDelta != null) {
      PrismReferenceValue orgRefValue = orgDelta.getAnyValue();

      try {
        PrismObject<OrgType> org =
            resolver.resolve(orgRefValue, "resolving parent org ref", null, null, result);
        OrgType orgType = org.asObjectable();
        ObjectReferenceType ref = orgType.getPasswordPolicyRef();
        if (ref != null) {
          LOGGER.trace("Org {} has specified password policy.", orgType);
          passwordPolicy =
              resolver.resolve(
                  ref,
                  ValuePolicyType.class,
                  null,
                  "resolving password policy for organization",
                  task,
                  result);
          LOGGER.trace("Resolved password policy {}", passwordPolicy);
        }

        if (passwordPolicy == null) {
          passwordPolicy = determineValuePolicy(org, task, result);
        }

      } catch (ObjectNotFoundException e) {
        throw new IllegalStateException(e);
      }
    }

    return passwordPolicy;
  }

  private ValuePolicyType determineValuePolicy(
      PrismObject object, Task task, OperationResult result) throws SchemaException {
    LOGGER.trace("Determining password policies from object", object);
    PrismReference orgRef = object.findReference(ObjectType.F_PARENT_ORG_REF);
    if (orgRef == null) {
      return null;
    }
    List<PrismReferenceValue> values = orgRef.getValues();
    ValuePolicyType valuePolicy = null;
    List<PrismObject<OrgType>> orgs = new ArrayList<PrismObject<OrgType>>();
    try {
      for (PrismReferenceValue orgRefValue : values) {
        if (orgRefValue != null) {

          if (valuePolicy != null) {
            throw new IllegalStateException(
                "Found more than one policy while trying to validate user's password. Please check your configuration");
          }

          PrismObject<OrgType> org =
              resolver.resolve(orgRefValue, "resolving parent org ref", null, null, result);
          orgs.add(org);
          valuePolicy = resolvePolicy(org, task, result);
        }
      }
    } catch (ObjectNotFoundException ex) {
      throw new IllegalStateException(ex);
    }
    // go deeper
    if (valuePolicy == null) {
      for (PrismObject<OrgType> orgType : orgs) {
        valuePolicy = determineValuePolicy(orgType, task, result);
        if (valuePolicy != null) {
          return valuePolicy;
        }
      }
    }
    return valuePolicy;
  }

  private ValuePolicyType resolvePolicy(PrismObject<OrgType> org, Task task, OperationResult result)
      throws SchemaException {
    try {
      OrgType orgType = org.asObjectable();
      ObjectReferenceType ref = orgType.getPasswordPolicyRef();
      if (ref == null) {
        return null;
      }

      return resolver.resolve(
          ref,
          ValuePolicyType.class,
          null,
          "resolving password policy for organization",
          task,
          result);

    } catch (ObjectNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      throw new IllegalStateException(e);
    }
  }

  <F extends ObjectType> void processPasswordPolicy(
      LensProjectionContext projectionContext,
      LensContext<F> context,
      Task task,
      OperationResult result)
      throws SchemaException, PolicyViolationException {

    ObjectDelta accountDelta = projectionContext.getDelta();

    if (accountDelta == null) {
      LOGGER.trace("Skipping processing password policies. Shadow delta not specified.");
      return;
    }

    if (ChangeType.DELETE == accountDelta.getChangeType()) {
      return;
    }

    PrismObject<ShadowType> accountShadow = null;
    PrismProperty<PasswordType> password = null;
    if (ChangeType.ADD == accountDelta.getChangeType()) {
      accountShadow = accountDelta.getObjectToAdd();
      if (accountShadow != null) {
        password = accountShadow.findProperty(SchemaConstants.PATH_PASSWORD_VALUE);
      }
    }
    if (ChangeType.MODIFY == accountDelta.getChangeType() || password == null) {
      PropertyDelta<PasswordType> passwordValueDelta = null;
      if (accountDelta != null) {
        passwordValueDelta = accountDelta.findPropertyDelta(SchemaConstants.PATH_PASSWORD_VALUE);
        // Modification sanity check
        if (accountDelta.getChangeType() == ChangeType.MODIFY
            && passwordValueDelta != null
            && (passwordValueDelta.isAdd() || passwordValueDelta.isDelete())) {
          throw new SchemaException(
              "Shadow password value cannot be added or deleted, it can only be replaced");
        }
        if (passwordValueDelta == null) {
          LOGGER.trace(
              "Skipping processing password policies. Shadow delta does not contain password change.");
          return;
        }
        password = (PrismProperty<PasswordType>) passwordValueDelta.getItemNewMatchingPath(null);
      }
    }

    //		PrismProperty<PasswordType> password = getPassword(projectionContext);
    ValuePolicyType passwordPolicy = null;
    if (isCheckOrgPolicy(context)) {
      passwordPolicy = determineValuePolicy(context.getFocusContext().getObjectAny(), task, result);
      context.getFocusContext().setOrgPasswordPolicy(passwordPolicy);
    } else {
      passwordPolicy = projectionContext.getEffectivePasswordPolicy();
    }

    processPasswordPolicy(passwordPolicy, password, result);
  }

  private <F extends ObjectType> boolean isCheckOrgPolicy(LensContext<F> context)
      throws SchemaException {
    LensFocusContext focusCtx = context.getFocusContext();
    if (focusCtx.getDelta() != null) {
      if (focusCtx.getDelta().isAdd()) {
        return false;
      }

      if (focusCtx.getDelta().isModify()
          && focusCtx.getDelta().hasItemDelta(SchemaConstants.PATH_PASSWORD_VALUE)) {
        return false;
      }
    }

    if (focusCtx.getOrgPasswordPolicy() != null) {
      return false;
    }

    return true;
  }

  // On missing password this returns empty string (""). It is then up to password policy whether it
  // allows empty passwords or not.
  private String determinePasswordValue(PrismProperty<PasswordType> password) {
    if (password == null || password.getValue(ProtectedStringType.class) == null) {
      return null;
    }

    ProtectedStringType passValue = password.getValue(ProtectedStringType.class).getValue();

    if (passValue == null) {
      return null;
    }

    String passwordStr = passValue.getClearValue();

    if (passwordStr == null && passValue.getEncryptedDataType() != null) {
      // TODO: is this appropriate handling???
      try {
        passwordStr = protector.decryptString(passValue);
      } catch (EncryptionException ex) {
        throw new SystemException("Failed to process password for user: ", ex);
      }
    }

    return passwordStr;
  }
}
Example #5
0
/** @author lazyman */
@PageDescriptor(
    url = "/admin/reports/create",
    action = {
      @AuthorizationAction(
          actionUri = PageAdminReports.AUTH_REPORTS_ALL,
          label = PageAdminConfiguration.AUTH_CONFIGURATION_ALL_LABEL,
          description = PageAdminConfiguration.AUTH_CONFIGURATION_ALL_DESCRIPTION),
      @AuthorizationAction(
          actionUri = AuthorizationConstants.AUTZ_UI_REPORTS_REPORT_CREATE_URL,
          label = "PageNewReport.auth.reports.label",
          description = "PageNewReport.auth.reports.description")
    })
public class PageNewReport extends PageAdminReports {

  private static final Trace LOGGER = TraceManager.getTrace(PageNewReport.class);

  private static final String ID_MAIN_FORM = "mainForm";
  private static final String ID_BUTTON_BAR = "buttonBar";
  private static final String ID_IMPORT_RADIO_GROUP = "importRadioGroup";
  private static final String ID_FILE_RADIO = "fileRadio";
  private static final String ID_XML_RADIO = "xmlRadio";
  private static final String ID_IMPORT_FILE_BUTTON = "importFileButton";
  private static final String ID_IMPORT_XML_BUTTON = "importXmlButton";
  private static final String ID_INPUT = "input";
  private static final String ID_INPUT_ACE = "inputAce";
  private static final String ID_ACE_EDITOR = "aceEditor";
  private static final String ID_INPUT_FILE_LABEL = "inputFileLabel";
  private static final String ID_INPUT_FILE = "inputFile";
  private static final String ID_FILE_INPUT = "fileInput";

  private static final String OPERATION_IMPORT_REPORT_XML = "Import Report from XML";
  private static final String OPERATION_IMPORT_REPORT = "Import Report from file";

  private static final Integer INPUT_FILE = 1;
  private static final Integer INPUT_XML = 2;

  private Model<String> xmlEditorModel;

  public PageNewReport() {
    xmlEditorModel = new Model<String>(null);

    initLayout();
  }

  private void initLayout() {
    Form mainForm = new Form(ID_MAIN_FORM);
    add(mainForm);

    final WebMarkupContainer input = new WebMarkupContainer(ID_INPUT);
    input.setOutputMarkupId(true);
    mainForm.add(input);

    final WebMarkupContainer buttonBar = new WebMarkupContainer(ID_BUTTON_BAR);
    buttonBar.setOutputMarkupId(true);
    mainForm.add(buttonBar);

    final IModel<Integer> groupModel = new Model<Integer>(INPUT_FILE);
    RadioGroup importRadioGroup = new RadioGroup(ID_IMPORT_RADIO_GROUP, groupModel);
    importRadioGroup.add(
        new AjaxFormChoiceComponentUpdatingBehavior() {

          @Override
          protected void onUpdate(AjaxRequestTarget target) {
            target.add(input);
            target.add(buttonBar);
          }
        });
    mainForm.add(importRadioGroup);

    Radio fileRadio = new Radio(ID_FILE_RADIO, new Model(INPUT_FILE), importRadioGroup);
    importRadioGroup.add(fileRadio);

    Radio xmlRadio = new Radio(ID_XML_RADIO, new Model(INPUT_XML), importRadioGroup);
    importRadioGroup.add(xmlRadio);

    WebMarkupContainer inputAce = new WebMarkupContainer(ID_INPUT_ACE);
    addVisibileForInputType(inputAce, INPUT_XML, groupModel);
    input.add(inputAce);

    AceEditor aceEditor = new AceEditor(ID_ACE_EDITOR, xmlEditorModel);
    aceEditor.setOutputMarkupId(true);
    inputAce.add(aceEditor);

    WebMarkupContainer inputFileLabel = new WebMarkupContainer(ID_INPUT_FILE_LABEL);
    addVisibileForInputType(inputFileLabel, INPUT_FILE, groupModel);
    input.add(inputFileLabel);

    WebMarkupContainer inputFile = new WebMarkupContainer(ID_INPUT_FILE);
    addVisibileForInputType(inputFile, INPUT_FILE, groupModel);
    input.add(inputFile);

    FileUploadField fileInput = new FileUploadField(ID_FILE_INPUT);
    inputFile.add(fileInput);

    initButtons(buttonBar, groupModel);
  }

  private void addVisibileForInputType(
      Component comp, final Integer type, final IModel<Integer> groupModel) {
    comp.add(
        new VisibleEnableBehaviour() {

          @Override
          public boolean isVisible() {
            return type.equals(groupModel.getObject());
          }
        });
  }

  private void initButtons(WebMarkupContainer buttonBar, IModel<Integer> inputType) {
    AjaxSubmitButton saveFileButton =
        new AjaxSubmitButton(
            ID_IMPORT_FILE_BUTTON, createStringResource("PageNewReport.button.import")) {

          @Override
          protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
            importReportFromFilePerformed(target);
          }

          @Override
          protected void onError(AjaxRequestTarget target, Form<?> form) {
            target.add(getFeedbackPanel());
          }
        };
    addVisibileForInputType(saveFileButton, INPUT_FILE, inputType);
    buttonBar.add(saveFileButton);

    AjaxSubmitButton saveXmlButton =
        new AjaxSubmitButton(
            ID_IMPORT_XML_BUTTON, createStringResource("PageNewReport.button.import")) {

          @Override
          protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
            importReportFromStreamPerformed(target);
          }

          @Override
          protected void onError(AjaxRequestTarget target, Form<?> form) {
            target.add(getFeedbackPanel());
          }
        };
    addVisibileForInputType(saveXmlButton, INPUT_XML, inputType);
    buttonBar.add(saveXmlButton);
  }

  private void importReportFromFilePerformed(AjaxRequestTarget target) {
    OperationResult result = new OperationResult(OPERATION_IMPORT_REPORT);

    FileUploadField file =
        (FileUploadField)
            get(createComponentPath(ID_MAIN_FORM, ID_INPUT, ID_INPUT_FILE, ID_FILE_INPUT));
    final FileUpload uploadedFile = file.getFileUpload();
    if (uploadedFile == null) {
      error(getString("PageNewReport.message.nullFile"));
      target.add(getFeedbackPanel());

      return;
    }

    InputStream stream = null;
    File newFile = null;
    try {
      // Create new file
      MidPointApplication application = getMidpointApplication();
      WebApplicationConfiguration config = application.getWebApplicationConfiguration();
      File folder = new File(config.getImportFolder());
      if (!folder.exists() || !folder.isDirectory()) {
        folder.mkdir();
      }

      newFile = new File(folder, uploadedFile.getClientFileName());
      // Check new file, delete if it already exists
      if (newFile.exists()) {
        newFile.delete();
      }
      // Save file
      //            Task task = createSimpleTask(OPERATION_IMPORT_FILE);
      newFile.createNewFile();
      uploadedFile.writeTo(newFile);

      InputStreamReader reader = new InputStreamReader(new FileInputStream(newFile), "utf-8");
      //            reader.
      stream = new ReaderInputStream(reader, reader.getEncoding());
      byte[] reportIn = IOUtils.toByteArray(stream);

      setResponsePage(new PageReport(new ReportDto(Base64.encodeBase64(reportIn))));
    } catch (Exception ex) {
      result.recordFatalError("Couldn't import file.", ex);
      LoggingUtils.logException(LOGGER, "Couldn't import file", ex);
    } finally {
      if (stream != null) {
        IOUtils.closeQuietly(stream);
      }
      if (newFile != null) {
        FileUtils.deleteQuietly(newFile);
      }
    }

    showResult(result);
    target.add(getFeedbackPanel());
  }

  private void importReportFromStreamPerformed(AjaxRequestTarget target) {
    String xml = xmlEditorModel.getObject();
    if (StringUtils.isEmpty(xml)) {
      error(getString("PageNewReport.message.emptyXml"));
      target.add(getFeedbackPanel());

      return;
    }

    OperationResult result = new OperationResult(OPERATION_IMPORT_REPORT_XML);
    InputStream stream = null;
    try {

      setResponsePage(new PageReport(new ReportDto(Base64.encodeBase64(xml.getBytes()))));
    } catch (Exception ex) {
      result.recordFatalError("Couldn't import object.", ex);
      LoggingUtils.logException(LOGGER, "Error occured during xml import", ex);
    } finally {
      if (stream != null) {
        IOUtils.closeQuietly(stream);
      }
    }

    if (result.isSuccess()) {
      xmlEditorModel.setObject(null);
    }

    showResult(result);
    target.add(getFeedbackPanel());
  }
}
/** @author semancik */
public class IcfConfigurationTransformer {

  private static final Trace LOGGER = TraceManager.getTrace(IcfConfigurationTransformer.class);

  private ConnectorType connectorType;
  private ConnectorInfo cinfo;
  private Protector protector;

  public IcfConfigurationTransformer(
      ConnectorType connectorType, ConnectorInfo cinfo, Protector protector) {
    super();
    this.connectorType = connectorType;
    this.cinfo = cinfo;
    this.protector = protector;
  }

  /**
   * Transforms midPoint XML configuration of the connector to the ICF configuration.
   *
   * <p>The "configuration" part of the XML resource definition will be used.
   *
   * <p>The provided ICF APIConfiguration will be modified, some values may be overwritten.
   *
   * @param apiConfig ICF connector configuration
   * @param resourceType midPoint XML configuration
   * @throws SchemaException
   * @throws ConfigurationException
   */
  public APIConfiguration transformConnectorConfiguration(PrismContainerValue configuration)
      throws SchemaException, ConfigurationException {

    APIConfiguration apiConfig = cinfo.createDefaultAPIConfiguration();
    ConfigurationProperties configProps = apiConfig.getConfigurationProperties();

    // The namespace of all the configuration properties specific to the
    // connector instance will have a connector instance namespace. This
    // namespace can be found in the resource definition.
    String connectorConfNs = connectorType.getNamespace();

    PrismContainer configurationPropertiesContainer =
        configuration.findContainer(
            ConnectorFactoryIcfImpl.CONNECTOR_SCHEMA_CONFIGURATION_PROPERTIES_ELEMENT_QNAME);
    if (configurationPropertiesContainer == null) {
      // Also try this. This is an older way.
      configurationPropertiesContainer =
          configuration.findContainer(
              new QName(
                  connectorConfNs,
                  ConnectorFactoryIcfImpl
                      .CONNECTOR_SCHEMA_CONFIGURATION_PROPERTIES_ELEMENT_LOCAL_NAME));
    }

    transformConnectorConfiguration(configProps, configurationPropertiesContainer, connectorConfNs);

    PrismContainer connectorPoolContainer =
        configuration.findContainer(
            new QName(
                ConnectorFactoryIcfImpl.NS_ICF_CONFIGURATION,
                ConnectorFactoryIcfImpl
                    .CONNECTOR_SCHEMA_CONNECTOR_POOL_CONFIGURATION_XML_ELEMENT_NAME));
    ObjectPoolConfiguration connectorPoolConfiguration = apiConfig.getConnectorPoolConfiguration();
    transformConnectorPoolConfiguration(connectorPoolConfiguration, connectorPoolContainer);

    PrismProperty producerBufferSizeProperty =
        configuration.findProperty(
            new QName(
                ConnectorFactoryIcfImpl.NS_ICF_CONFIGURATION,
                ConnectorFactoryIcfImpl.CONNECTOR_SCHEMA_PRODUCER_BUFFER_SIZE_XML_ELEMENT_NAME));
    if (producerBufferSizeProperty != null) {
      apiConfig.setProducerBufferSize(parseInt(producerBufferSizeProperty));
    }

    PrismContainer connectorTimeoutsContainer =
        configuration.findContainer(
            new QName(
                ConnectorFactoryIcfImpl.NS_ICF_CONFIGURATION,
                ConnectorFactoryIcfImpl.CONNECTOR_SCHEMA_TIMEOUTS_XML_ELEMENT_NAME));
    transformConnectorTimeoutsConfiguration(apiConfig, connectorTimeoutsContainer);

    PrismContainer resultsHandlerConfigurationContainer =
        configuration.findContainer(
            new QName(
                ConnectorFactoryIcfImpl.NS_ICF_CONFIGURATION,
                ConnectorFactoryIcfImpl
                    .CONNECTOR_SCHEMA_RESULTS_HANDLER_CONFIGURATION_ELEMENT_LOCAL_NAME));
    ResultsHandlerConfiguration resultsHandlerConfiguration =
        apiConfig.getResultsHandlerConfiguration();
    transformResultsHandlerConfiguration(
        resultsHandlerConfiguration, resultsHandlerConfigurationContainer);

    return apiConfig;
  }

  private void transformConnectorConfiguration(
      ConfigurationProperties configProps,
      PrismContainer<?> configurationPropertiesContainer,
      String connectorConfNs)
      throws ConfigurationException, SchemaException {

    if (configurationPropertiesContainer == null
        || configurationPropertiesContainer.getValue() == null) {
      throw new SchemaException("No configuration properties container in " + connectorType);
    }

    int numConfingProperties = 0;
    List<QName> wrongNamespaceProperties = new ArrayList<>();

    for (PrismProperty prismProperty :
        configurationPropertiesContainer.getValue().getProperties()) {
      QName propertyQName = prismProperty.getElementName();

      // All the elements must be in a connector instance
      // namespace.
      if (propertyQName.getNamespaceURI() == null
          || !propertyQName.getNamespaceURI().equals(connectorConfNs)) {
        LOGGER.warn(
            "Found element with a wrong namespace ({}) in {}",
            propertyQName.getNamespaceURI(),
            connectorType);
        wrongNamespaceProperties.add(propertyQName);
      } else {

        numConfingProperties++;

        // Local name of the element is the same as the name
        // of ICF configuration property
        String propertyName = propertyQName.getLocalPart();
        ConfigurationProperty property = configProps.getProperty(propertyName);

        if (property == null) {
          throw new ConfigurationException("Unknown configuration property " + propertyName);
        }

        // Check (java) type of ICF configuration property,
        // behave accordingly
        Class<?> type = property.getType();
        if (type.isArray()) {
          property.setValue(convertToIcfArray(prismProperty, type.getComponentType()));
          // property.setValue(prismProperty.getRealValuesArray(type.getComponentType()));
        } else {
          // Single-valued property are easy to convert
          property.setValue(convertToIcfSingle(prismProperty, type));
          // property.setValue(prismProperty.getRealValue(type));
        }
      }
    }
    // empty configuration is OK e.g. when creating a new resource using wizard
    if (numConfingProperties == 0 && !wrongNamespaceProperties.isEmpty()) {
      throw new SchemaException(
          "No configuration properties found. Wrong namespace? (expected: "
              + connectorConfNs
              + ", present e.g. "
              + wrongNamespaceProperties.get(0)
              + ")");
    }
  }

  private void transformConnectorPoolConfiguration(
      ObjectPoolConfiguration connectorPoolConfiguration, PrismContainer<?> connectorPoolContainer)
      throws SchemaException {

    if (connectorPoolContainer == null || connectorPoolContainer.getValue() == null) {
      return;
    }

    for (PrismProperty prismProperty : connectorPoolContainer.getValue().getProperties()) {
      QName propertyQName = prismProperty.getElementName();
      if (propertyQName.getNamespaceURI().equals(ConnectorFactoryIcfImpl.NS_ICF_CONFIGURATION)) {
        String subelementName = propertyQName.getLocalPart();
        if (ConnectorFactoryIcfImpl
            .CONNECTOR_SCHEMA_CONNECTOR_POOL_CONFIGURATION_MIN_EVICTABLE_IDLE_TIME_MILLIS.equals(
            subelementName)) {
          connectorPoolConfiguration.setMinEvictableIdleTimeMillis(parseLong(prismProperty));
        } else if (ConnectorFactoryIcfImpl.CONNECTOR_SCHEMA_CONNECTOR_POOL_CONFIGURATION_MIN_IDLE
            .equals(subelementName)) {
          connectorPoolConfiguration.setMinIdle(parseInt(prismProperty));
        } else if (ConnectorFactoryIcfImpl.CONNECTOR_SCHEMA_CONNECTOR_POOL_CONFIGURATION_MAX_IDLE
            .equals(subelementName)) {
          connectorPoolConfiguration.setMaxIdle(parseInt(prismProperty));
        } else if (ConnectorFactoryIcfImpl.CONNECTOR_SCHEMA_CONNECTOR_POOL_CONFIGURATION_MAX_OBJECTS
            .equals(subelementName)) {
          connectorPoolConfiguration.setMaxObjects(parseInt(prismProperty));
        } else if (ConnectorFactoryIcfImpl.CONNECTOR_SCHEMA_CONNECTOR_POOL_CONFIGURATION_MAX_WAIT
            .equals(subelementName)) {
          connectorPoolConfiguration.setMaxWait(parseLong(prismProperty));
        } else {
          throw new SchemaException(
              "Unexpected element "
                  + propertyQName
                  + " in "
                  + ConnectorFactoryIcfImpl
                      .CONNECTOR_SCHEMA_CONNECTOR_POOL_CONFIGURATION_XML_ELEMENT_NAME);
        }
      } else {
        throw new SchemaException(
            "Unexpected element "
                + propertyQName
                + " in "
                + ConnectorFactoryIcfImpl
                    .CONNECTOR_SCHEMA_CONNECTOR_POOL_CONFIGURATION_XML_ELEMENT_NAME);
      }
    }
  }

  private void transformConnectorTimeoutsConfiguration(
      APIConfiguration apiConfig, PrismContainer<?> connectorTimeoutsContainer)
      throws SchemaException {

    if (connectorTimeoutsContainer == null || connectorTimeoutsContainer.getValue() == null) {
      return;
    }

    for (PrismProperty prismProperty : connectorTimeoutsContainer.getValue().getProperties()) {
      QName propertQName = prismProperty.getElementName();

      if (ConnectorFactoryIcfImpl.NS_ICF_CONFIGURATION.equals(propertQName.getNamespaceURI())) {
        String opName = propertQName.getLocalPart();
        Class<? extends APIOperation> apiOpClass =
            ConnectorFactoryIcfImpl.resolveApiOpClass(opName);
        if (apiOpClass != null) {
          apiConfig.setTimeout(apiOpClass, parseInt(prismProperty));
        } else {
          throw new SchemaException(
              "Unknown operation name "
                  + opName
                  + " in "
                  + ConnectorFactoryIcfImpl.CONNECTOR_SCHEMA_TIMEOUTS_XML_ELEMENT_NAME);
        }
      }
    }
  }

  private void transformResultsHandlerConfiguration(
      ResultsHandlerConfiguration resultsHandlerConfiguration,
      PrismContainer<?> resultsHandlerConfigurationContainer)
      throws SchemaException {

    if (resultsHandlerConfigurationContainer == null
        || resultsHandlerConfigurationContainer.getValue() == null) {
      return;
    }

    for (PrismProperty prismProperty :
        resultsHandlerConfigurationContainer.getValue().getProperties()) {
      QName propertyQName = prismProperty.getElementName();
      if (propertyQName.getNamespaceURI().equals(ConnectorFactoryIcfImpl.NS_ICF_CONFIGURATION)) {
        String subelementName = propertyQName.getLocalPart();
        if (ConnectorFactoryIcfImpl
            .CONNECTOR_SCHEMA_RESULTS_HANDLER_CONFIGURATION_ENABLE_NORMALIZING_RESULTS_HANDLER
            .equals(subelementName)) {
          resultsHandlerConfiguration.setEnableNormalizingResultsHandler(
              parseBoolean(prismProperty));
        } else if (ConnectorFactoryIcfImpl
            .CONNECTOR_SCHEMA_RESULTS_HANDLER_CONFIGURATION_ENABLE_FILTERED_RESULTS_HANDLER.equals(
            subelementName)) {
          resultsHandlerConfiguration.setEnableFilteredResultsHandler(parseBoolean(prismProperty));
        } else if (ConnectorFactoryIcfImpl
            .CONNECTOR_SCHEMA_RESULTS_HANDLER_CONFIGURATION_FILTERED_RESULTS_HANDLER_IN_VALIDATION_MODE
            .equals(subelementName)) {
          resultsHandlerConfiguration.setFilteredResultsHandlerInValidationMode(
              parseBoolean(prismProperty));
        } else if (ConnectorFactoryIcfImpl
            .CONNECTOR_SCHEMA_RESULTS_HANDLER_CONFIGURATION_ENABLE_CASE_INSENSITIVE_HANDLER.equals(
            subelementName)) {
          resultsHandlerConfiguration.setEnableCaseInsensitiveFilter(parseBoolean(prismProperty));
        } else if (ConnectorFactoryIcfImpl
            .CONNECTOR_SCHEMA_RESULTS_HANDLER_CONFIGURATION_ENABLE_ATTRIBUTES_TO_GET_SEARCH_RESULTS_HANDLER
            .equals(subelementName)) {
          resultsHandlerConfiguration.setEnableAttributesToGetSearchResultsHandler(
              parseBoolean(prismProperty));
        } else {
          throw new SchemaException(
              "Unexpected element "
                  + propertyQName
                  + " in "
                  + ConnectorFactoryIcfImpl
                      .CONNECTOR_SCHEMA_RESULTS_HANDLER_CONFIGURATION_ELEMENT_LOCAL_NAME);
        }
      } else {
        throw new SchemaException(
            "Unexpected element "
                + propertyQName
                + " in "
                + ConnectorFactoryIcfImpl
                    .CONNECTOR_SCHEMA_RESULTS_HANDLER_CONFIGURATION_ELEMENT_LOCAL_NAME);
      }
    }
  }

  private int parseInt(PrismProperty<?> prop) {
    return prop.getRealValue(Integer.class);
  }

  private long parseLong(PrismProperty<?> prop) {
    Object realValue = prop.getRealValue();
    if (realValue instanceof Long) {
      return (Long) realValue;
    } else if (realValue instanceof Integer) {
      return ((Integer) realValue);
    } else {
      throw new IllegalArgumentException("Cannot convert " + realValue.getClass() + " to long");
    }
  }

  private boolean parseBoolean(PrismProperty<?> prop) {
    return prop.getRealValue(Boolean.class);
  }

  private Object convertToIcfSingle(PrismProperty<?> configProperty, Class<?> expectedType)
      throws ConfigurationException {
    if (configProperty == null) {
      return null;
    }
    PrismPropertyValue<?> pval = configProperty.getValue();
    return convertToIcf(pval, expectedType);
  }

  private Object[] convertToIcfArray(PrismProperty prismProperty, Class<?> componentType)
      throws ConfigurationException {
    List<PrismPropertyValue> values = prismProperty.getValues();
    Object valuesArrary = Array.newInstance(componentType, values.size());
    for (int j = 0; j < values.size(); ++j) {
      Object icfValue = convertToIcf(values.get(j), componentType);
      Array.set(valuesArrary, j, icfValue);
    }
    return (Object[]) valuesArrary;
  }

  private Object convertToIcf(PrismPropertyValue<?> pval, Class<?> expectedType)
      throws ConfigurationException {
    Object midPointRealValue = pval.getValue();
    if (expectedType.equals(GuardedString.class)) {
      // Guarded string is a special ICF beast
      // The value must be ProtectedStringType
      if (midPointRealValue instanceof ProtectedStringType) {
        ProtectedStringType ps = (ProtectedStringType) pval.getValue();
        return IcfUtil.toGuardedString(
            ps, pval.getParent().getElementName().getLocalPart(), protector);
      } else {
        throw new ConfigurationException(
            "Expected protected string as value of configuration property "
                + pval.getParent().getElementName().getLocalPart()
                + " but got "
                + midPointRealValue.getClass());
      }

    } else if (expectedType.equals(GuardedByteArray.class)) {
      // Guarded string is a special ICF beast
      // TODO
      //			return new GuardedByteArray(Base64.decodeBase64((ProtectedByteArrayType)
      // pval.getValue()));
      return new GuardedByteArray(((ProtectedByteArrayType) pval.getValue()).getClearBytes());
    } else if (midPointRealValue instanceof PolyString) {
      return ((PolyString) midPointRealValue).getOrig();
    } else if (midPointRealValue instanceof PolyStringType) {
      return ((PolyStringType) midPointRealValue).getOrig();
    } else if (expectedType.equals(File.class) && midPointRealValue instanceof String) {
      return new File((String) midPointRealValue);
    } else if (expectedType.equals(String.class)
        && midPointRealValue instanceof ProtectedStringType) {
      try {
        return protector.decryptString((ProtectedStringType) midPointRealValue);
      } catch (EncryptionException e) {
        throw new ConfigurationException(e);
      }
    } else {
      return midPointRealValue;
    }
  }
}
/**
 * Test UCF implementation with OpenDJ and ICF LDAP connector.
 *
 * <p>This test is using embedded OpenDJ as a resource and ICF LDAP connector. The test is executed
 * by direct calls to the UCF interface.
 *
 * @author Radovan Semancik
 * @author Katka Valalikova
 *     <p>This is an UCF test. It shold not need repository or other things from the midPoint spring
 *     context except from the provisioning beans. But due to a general issue with spring context
 *     initialization this is a lesser evil for now (MID-392)
 */
@ContextConfiguration(locations = {"classpath:ctx-provisioning-test-no-repo.xml"})
public class TestUcfOpenDj extends AbstractTestNGSpringContextTests {

  private static final String FILENAME_RESOURCE_OPENDJ =
      "src/test/resources/object/resource-opendj.xml";
  private static final String FILENAME_RESOURCE_OPENDJ_BAD =
      "src/test/resources/object/resource-opendj-bad.xml";
  private static final String FILENAME_CONNECTOR_LDAP = "src/test/resources/ucf/connector-ldap.xml";

  private ResourceType resourceType;
  private ResourceType badResourceType;
  private ConnectorType connectorType;
  private ConnectorFactory factory;
  private ConnectorInstance cc;
  private PrismSchema connectorSchema;
  private ResourceSchema resourceSchema;

  private static Trace LOGGER = TraceManager.getTrace(TestUcfOpenDj.class);

  @Autowired(required = true)
  ConnectorFactory connectorFactoryIcfImpl;

  @Autowired(required = true)
  Protector protector;

  @Autowired(required = true)
  PrismContext prismContext;

  protected static OpenDJController openDJController = new OpenDJController();

  public TestUcfOpenDj() throws JAXBException {
    System.setProperty("midpoint.home", "target/midPointHome/");
  }

  @BeforeSuite
  public void setup() throws SchemaException, SAXException, IOException {
    PrettyPrinter.setDefaultNamespacePrefix(MidPointConstants.NS_MIDPOINT_PUBLIC_PREFIX);
    PrismTestUtil.resetPrismContext(MidPointPrismContextFactory.FACTORY);
  }

  @BeforeClass
  public static void startLdap() throws Exception {
    LOGGER.info("------------------------------------------------------------------------------");
    LOGGER.info("START:  OpenDjUcfTest");
    LOGGER.info("------------------------------------------------------------------------------");
    openDJController.startCleanServer();
  }

  @AfterClass
  public static void stopLdap() throws Exception {
    openDJController.stop();
    LOGGER.info("------------------------------------------------------------------------------");
    LOGGER.info("STOP:  OpenDjUcfTest");
    LOGGER.info("------------------------------------------------------------------------------");
  }

  @BeforeMethod
  public void initUcf() throws Exception {
    TestUtil.displayTestTile("initUcf");

    File file = new File(FILENAME_RESOURCE_OPENDJ);
    FileInputStream fis = new FileInputStream(file);

    // Resource
    PrismObject<ResourceType> resource =
        PrismTestUtil.parseObject(new File(FILENAME_RESOURCE_OPENDJ));
    resourceType = resource.asObjectable();

    // Resource: Second copy for negative test cases
    PrismObject<ResourceType> badResource =
        PrismTestUtil.parseObject(new File(FILENAME_RESOURCE_OPENDJ_BAD));
    badResourceType = badResource.asObjectable();

    // Connector
    PrismObject<ConnectorType> connector =
        PrismTestUtil.parseObject(new File(FILENAME_CONNECTOR_LDAP));
    connectorType = connector.asObjectable();

    factory = connectorFactoryIcfImpl;

    cc =
        factory.createConnectorInstance(
            connectorType, ResourceTypeUtil.getResourceNamespace(resourceType), "test connector");
    AssertJUnit.assertNotNull("Cannot create connector instance", cc);

    connectorSchema = cc.generateConnectorSchema();
    AssertJUnit.assertNotNull("Cannot generate connector schema", cc);
    display("Connector schema", connectorSchema);

    OperationResult result = new OperationResult("initUcf");
    cc.configure(resourceType.getConnectorConfiguration().asPrismContainerValue(), result);
    cc.initialize(null, null, false, result);
    // TODO: assert something

    resourceSchema = cc.fetchResourceSchema(null, result);
    display("Resource schema", resourceSchema);

    AssertJUnit.assertNotNull(resourceSchema);
  }

  @AfterMethod
  public void shutdownUcf() throws Exception {}

  @Test
  public void test010ConnectorSchemaSanity() throws Exception {
    final String TEST_NAME = "test010ConnectorSchemaSanity";
    TestUtil.displayTestTile(TEST_NAME);

    ProvisioningTestUtil.assertConnectorSchemaSanity(connectorSchema, "LDAP connector");
  }

  @Test
  public void test020ResourceSchemaSanity() throws Exception {
    final String TEST_NAME = "test020ResourceSchemaSanity";
    TestUtil.displayTestTile(TEST_NAME);

    QName objectClassQname =
        new QName(
            ResourceTypeUtil.getResourceNamespace(resourceType),
            ProvisioningTestUtil.OBJECT_CLASS_INETORGPERSON_NAME);
    ObjectClassComplexTypeDefinition accountDefinition =
        resourceSchema.findObjectClassDefinition(objectClassQname);
    assertNotNull("No object class definition " + objectClassQname, accountDefinition);
    //		assertEquals("Object class " + objectClassQname + " is not account", ShadowKindType.ACCOUNT,
    // accountDefinition.getKind());
    //		assertTrue("Object class " + objectClassQname + " is not default account",
    // accountDefinition.isDefaultInAKind());
    assertFalse("Object class " + objectClassQname + " is empty", accountDefinition.isEmpty());
    assertFalse("Object class " + objectClassQname + " is empty", accountDefinition.isIgnored());

    Collection<? extends ResourceAttributeDefinition> identifiers =
        accountDefinition.getIdentifiers();
    assertNotNull("Null identifiers for " + objectClassQname, identifiers);
    assertFalse("Empty identifiers for " + objectClassQname, identifiers.isEmpty());

    ResourceAttributeDefinition<String> idPrimaryDef =
        accountDefinition.findAttributeDefinition(
            new QName(
                ResourceTypeUtil.getResourceNamespace(resourceType),
                ProvisioningTestUtil.RESOURCE_OPENDJ_PRIMARY_IDENTIFIER_LOCAL_NAME));
    assertNotNull(
        "No definition for attribute "
            + ProvisioningTestUtil.RESOURCE_OPENDJ_PRIMARY_IDENTIFIER_LOCAL_NAME,
        idPrimaryDef);
    assertTrue(
        "Attribute "
            + ProvisioningTestUtil.RESOURCE_OPENDJ_PRIMARY_IDENTIFIER_LOCAL_NAME
            + " in not an identifier",
        idPrimaryDef.isIdentifier(accountDefinition));
    assertTrue(
        "Attribute "
            + ProvisioningTestUtil.RESOURCE_OPENDJ_PRIMARY_IDENTIFIER_LOCAL_NAME
            + " in not in identifiers list",
        identifiers.contains(idPrimaryDef));
    assertEquals(
        "Attribute "
            + ProvisioningTestUtil.RESOURCE_OPENDJ_PRIMARY_IDENTIFIER_LOCAL_NAME
            + " has wrong native name",
        ProvisioningTestUtil.RESOURCE_OPENDJ_PRIMARY_IDENTIFIER_LOCAL_NAME,
        idPrimaryDef.getNativeAttributeName());
    assertEquals(
        "Attribute "
            + ProvisioningTestUtil.RESOURCE_OPENDJ_PRIMARY_IDENTIFIER_LOCAL_NAME
            + " has wrong framework name",
        Uid.NAME,
        idPrimaryDef.getFrameworkAttributeName());

    ResourceAttributeDefinition<String> idSecondaryDef =
        accountDefinition.findAttributeDefinition(
            new QName(
                ResourceTypeUtil.getResourceNamespace(resourceType),
                ProvisioningTestUtil.RESOURCE_OPENDJ_SECONDARY_IDENTIFIER_LOCAL_NAME));
    assertNotNull(
        "No definition for attribute " + ConnectorFactoryIcfImpl.ICFS_NAME, idSecondaryDef);
    assertTrue(
        "Attribute "
            + ProvisioningTestUtil.RESOURCE_OPENDJ_SECONDARY_IDENTIFIER_LOCAL_NAME
            + " in not secondary identifier",
        idSecondaryDef.isSecondaryIdentifier(accountDefinition));
    assertFalse(
        "Attribute "
            + ProvisioningTestUtil.RESOURCE_OPENDJ_SECONDARY_IDENTIFIER_LOCAL_NAME
            + " in in identifiers list and it should NOT be",
        identifiers.contains(idSecondaryDef));
    assertTrue(
        "Attribute "
            + ProvisioningTestUtil.RESOURCE_OPENDJ_SECONDARY_IDENTIFIER_LOCAL_NAME
            + " in not in secomdary identifiers list",
        accountDefinition.getSecondaryIdentifiers().contains(idSecondaryDef));
    assertEquals(
        "Attribute "
            + ProvisioningTestUtil.RESOURCE_OPENDJ_SECONDARY_IDENTIFIER_LOCAL_NAME
            + " has wrong native name",
        ProvisioningTestUtil.RESOURCE_OPENDJ_SECONDARY_IDENTIFIER_LOCAL_NAME,
        idSecondaryDef.getNativeAttributeName());
    assertEquals(
        "Attribute "
            + ProvisioningTestUtil.RESOURCE_OPENDJ_SECONDARY_IDENTIFIER_LOCAL_NAME
            + " has wrong framework name",
        Name.NAME,
        idSecondaryDef.getFrameworkAttributeName());

    assertEquals("Unexpected identifiers: " + identifiers, 1, identifiers.size());
    assertEquals(
        "Unexpected secondary identifiers: " + accountDefinition.getSecondaryIdentifiers(),
        1,
        accountDefinition.getSecondaryIdentifiers().size());
  }

  private Collection<ResourceAttribute<?>> addSampleResourceObject(
      String name, String givenName, String familyName)
      throws CommunicationException, GenericFrameworkException, SchemaException,
          ObjectAlreadyExistsException, ConfigurationException {
    OperationResult result = new OperationResult(this.getClass().getName() + ".testAdd");

    QName objectClassQname =
        new QName(
            ResourceTypeUtil.getResourceNamespace(resourceType),
            ProvisioningTestUtil.OBJECT_CLASS_INETORGPERSON_NAME);
    ObjectClassComplexTypeDefinition accountDefinition =
        resourceSchema.findObjectClassDefinition(objectClassQname);
    assertNotNull("No object class definition " + objectClassQname, accountDefinition);
    ResourceAttributeContainer resourceObject =
        accountDefinition.instantiate(ShadowType.F_ATTRIBUTES);

    ResourceAttributeDefinition<String> attributeDefinition =
        accountDefinition.findAttributeDefinition(
            new QName(
                ResourceTypeUtil.getResourceNamespace(resourceType),
                ProvisioningTestUtil.RESOURCE_OPENDJ_SECONDARY_IDENTIFIER_LOCAL_NAME));
    ResourceAttribute<String> attribute = attributeDefinition.instantiate();
    attribute.setValue(
        new PrismPropertyValue<String>("uid=" + name + ",ou=people,dc=example,dc=com"));
    resourceObject.add(attribute);

    attributeDefinition =
        accountDefinition.findAttributeDefinition(
            new QName(ResourceTypeUtil.getResourceNamespace(resourceType), "sn"));
    attribute = attributeDefinition.instantiate();
    attribute.setValue(new PrismPropertyValue(familyName));
    resourceObject.add(attribute);

    attributeDefinition =
        accountDefinition.findAttributeDefinition(
            new QName(ResourceTypeUtil.getResourceNamespace(resourceType), "cn"));
    attribute = attributeDefinition.instantiate();
    attribute.setValue(new PrismPropertyValue(givenName + " " + familyName));
    resourceObject.add(attribute);

    attributeDefinition =
        accountDefinition.findAttributeDefinition(
            new QName(ResourceTypeUtil.getResourceNamespace(resourceType), "givenName"));
    attribute = attributeDefinition.instantiate();
    attribute.setValue(new PrismPropertyValue(givenName));
    resourceObject.add(attribute);

    PrismObject<ShadowType> shadow = wrapInShadow(ShadowType.class, resourceObject);

    Set<Operation> operation = new HashSet<Operation>();
    Collection<ResourceAttribute<?>> resourceAttributes = cc.addObject(shadow, operation, result);
    return resourceAttributes;
  }

  private String getEntryUuid(Collection<ResourceAttribute<?>> identifiers) {
    for (ResourceAttribute<?> identifier : identifiers) {
      if (identifier
          .getElementName()
          .equals(
              new QName(
                  ResourceTypeUtil.getResourceNamespace(resourceType),
                  ProvisioningTestUtil.RESOURCE_OPENDJ_PRIMARY_IDENTIFIER_LOCAL_NAME))) {
        return identifier.getValue(String.class).getValue();
      }
    }
    return null;
  }

  @Test
  public void test100AddDeleteObject() throws Exception {
    final String TEST_NAME = "test100AddDeleteObject";
    TestUtil.displayTestTile(this, TEST_NAME);

    OperationResult result = new OperationResult(this.getClass().getName() + "." + TEST_NAME);

    Collection<ResourceAttribute<?>> identifiers = addSampleResourceObject("john", "John", "Smith");

    String uid = null;
    for (ResourceAttribute<?> resourceAttribute : identifiers) {
      if (ConnectorFactoryIcfImpl.ICFS_UID.equals(resourceAttribute.getElementName())) {
        uid = resourceAttribute.getValue(String.class).getValue();
        System.out.println("uuuuid:" + uid);
        assertNotNull(uid);
      }
    }

    ObjectClassComplexTypeDefinition accountDefinition =
        resourceSchema.findObjectClassDefinition(
            ProvisioningTestUtil.OBJECT_CLASS_INETORGPERSON_NAME);

    cc.deleteObject(accountDefinition, null, identifiers, result);

    ResourceObjectIdentification identification =
        new ResourceObjectIdentification(accountDefinition, identifiers);
    PrismObject<ShadowType> resObj = null;
    try {
      resObj = cc.fetchObject(ShadowType.class, identification, null, result);
      Assert.fail();
    } catch (ObjectNotFoundException ex) {
      AssertJUnit.assertNull(resObj);
    }
  }

  @Test
  public void test110ChangeModifyObject() throws Exception {
    final String TEST_NAME = "test110ChangeModifyObject";
    TestUtil.displayTestTile(this, TEST_NAME);

    OperationResult result = new OperationResult(this.getClass().getName() + "." + TEST_NAME);

    Collection<ResourceAttribute<?>> identifiers = addSampleResourceObject("john", "John", "Smith");

    Set<Operation> changes = new HashSet<Operation>();

    changes.add(createAddAttributeChange("employeeNumber", "123123123"));
    changes.add(createReplaceAttributeChange("sn", "Smith007"));
    changes.add(createAddAttributeChange("street", "Wall Street"));
    changes.add(createDeleteAttributeChange("givenName", "John"));

    ObjectClassComplexTypeDefinition accountDefinition =
        resourceSchema.findObjectClassDefinition(
            ProvisioningTestUtil.OBJECT_CLASS_INETORGPERSON_NAME);

    cc.modifyObject(accountDefinition, identifiers, changes, result);

    ResourceObjectIdentification identification =
        new ResourceObjectIdentification(accountDefinition, identifiers);
    PrismObject<ShadowType> shadow = cc.fetchObject(ShadowType.class, identification, null, result);
    ResourceAttributeContainer resObj = ShadowUtil.getAttributesContainer(shadow);

    AssertJUnit.assertNull(
        resObj.findAttribute(
            new QName(ResourceTypeUtil.getResourceNamespace(resourceType), "givenName")));

    String addedEmployeeNumber =
        resObj
            .findAttribute(
                new QName(ResourceTypeUtil.getResourceNamespace(resourceType), "employeeNumber"))
            .getValue(String.class)
            .getValue();
    String changedSn =
        resObj
            .findAttribute(new QName(ResourceTypeUtil.getResourceNamespace(resourceType), "sn"))
            .getValues(String.class)
            .iterator()
            .next()
            .getValue();
    String addedStreet =
        resObj
            .findAttribute(new QName(ResourceTypeUtil.getResourceNamespace(resourceType), "street"))
            .getValues(String.class)
            .iterator()
            .next()
            .getValue();

    System.out.println("changed employee number: " + addedEmployeeNumber);
    System.out.println("changed sn: " + changedSn);
    System.out.println("added street: " + addedStreet);

    AssertJUnit.assertEquals("123123123", addedEmployeeNumber);
    AssertJUnit.assertEquals("Smith007", changedSn);
    AssertJUnit.assertEquals("Wall Street", addedStreet);
  }

  @Test
  public void test200FetchChanges() throws Exception {
    final String TEST_NAME = "test200FetchChanges";
    TestUtil.displayTestTile(this, TEST_NAME);

    OperationResult result = new OperationResult(this.getClass().getName() + "." + TEST_NAME);
    ObjectClassComplexTypeDefinition accountDefinition =
        resourceSchema.findObjectClassDefinition(
            ProvisioningTestUtil.OBJECT_CLASS_INETORGPERSON_NAME);
    PrismProperty<Integer> lastToken = cc.fetchCurrentToken(accountDefinition, result);

    System.out.println("Property:");
    System.out.println(SchemaDebugUtil.prettyPrint(lastToken));
    System.out.println("token " + lastToken.toString());

    assertNotNull("No last token", lastToken);
    assertNotNull("No last token value", lastToken.getRealValue());

    List<Change<ShadowType>> changes = cc.fetchChanges(accountDefinition, lastToken, null, result);
    AssertJUnit.assertEquals(0, changes.size());
  }

  private PrismProperty createProperty(String propertyName, String propertyValue) {
    ObjectClassComplexTypeDefinition accountDefinition =
        resourceSchema.findObjectClassDefinition(
            new QName(
                ResourceTypeUtil.getResourceNamespace(resourceType),
                ProvisioningTestUtil.OBJECT_CLASS_INETORGPERSON_NAME));
    ResourceAttributeDefinition propertyDef =
        accountDefinition.findAttributeDefinition(
            new QName(ResourceTypeUtil.getResourceNamespace(resourceType), propertyName));
    ResourceAttribute property = propertyDef.instantiate();
    property.setValue(new PrismPropertyValue(propertyValue));
    return property;
  }

  private PropertyModificationOperation createReplaceAttributeChange(
      String propertyName, String propertyValue) {
    PrismProperty property = createProperty(propertyName, propertyValue);
    ItemPath propertyPath =
        new ItemPath(
            ShadowType.F_ATTRIBUTES,
            new QName(ResourceTypeUtil.getResourceNamespace(resourceType), propertyName));
    PropertyDelta delta = new PropertyDelta(propertyPath, property.getDefinition(), prismContext);
    delta.setValueToReplace(new PrismPropertyValue(propertyValue));
    PropertyModificationOperation attributeModification = new PropertyModificationOperation(delta);
    return attributeModification;
  }

  private PropertyModificationOperation createAddAttributeChange(
      String propertyName, String propertyValue) {
    PrismProperty property = createProperty(propertyName, propertyValue);
    ItemPath propertyPath =
        new ItemPath(
            ShadowType.F_ATTRIBUTES,
            new QName(ResourceTypeUtil.getResourceNamespace(resourceType), propertyName));
    PropertyDelta delta = new PropertyDelta(propertyPath, property.getDefinition(), prismContext);
    delta.addValueToAdd(new PrismPropertyValue(propertyValue));
    PropertyModificationOperation attributeModification = new PropertyModificationOperation(delta);
    return attributeModification;
  }

  private PropertyModificationOperation createDeleteAttributeChange(
      String propertyName, String propertyValue) {
    PrismProperty property = createProperty(propertyName, propertyValue);
    ItemPath propertyPath =
        new ItemPath(
            ShadowType.F_ATTRIBUTES,
            new QName(ResourceTypeUtil.getResourceNamespace(resourceType), propertyName));
    PropertyDelta delta = new PropertyDelta(propertyPath, property.getDefinition(), prismContext);
    delta.addValueToDelete(new PrismPropertyValue(propertyValue));
    PropertyModificationOperation attributeModification = new PropertyModificationOperation(delta);
    return attributeModification;
  }

  private PropertyModificationOperation createActivationChange(ActivationStatusType status) {
    PrismObjectDefinition<ShadowType> shadowDefinition = getShadowDefinition(ShadowType.class);
    PropertyDelta<ActivationStatusType> delta =
        PropertyDelta.createDelta(
            new ItemPath(ShadowType.F_ACTIVATION, ActivationType.F_ADMINISTRATIVE_STATUS),
            shadowDefinition);
    delta.setValueToReplace(new PrismPropertyValue<ActivationStatusType>(status));
    return new PropertyModificationOperation(delta);
  }

  /**
   * Simple call to connector test() method.
   *
   * @throws Exception
   */
  @Test
  public void test300TestConnection() throws Exception {
    final String TEST_NAME = "test300TestConnection";
    TestUtil.displayTestTile(this, TEST_NAME);
    // GIVEN

    OperationResult result = new OperationResult(TEST_NAME);

    // WHEN

    cc.test(result);

    // THEN
    result.computeStatus("test failed");
    AssertJUnit.assertNotNull(result);
    OperationResult connectorConnectionResult = result.getSubresults().get(0);
    AssertJUnit.assertNotNull(connectorConnectionResult);
    System.out.println("Test \"connector connection\" result: " + connectorConnectionResult);
    AssertJUnit.assertTrue(connectorConnectionResult.isSuccess());
    AssertJUnit.assertTrue(result.isSuccess());
  }

  /**
   * Simple call to connector test() method.
   *
   * @throws Exception
   */
  @Test
  public void test310TestConnectionNegative() throws Exception {
    final String TEST_NAME = "test310TestConnectionNegative";
    TestUtil.displayTestTile(this, TEST_NAME);
    // GIVEN

    OperationResult result = new OperationResult(TEST_NAME);

    ConnectorInstance badConnector =
        factory.createConnectorInstance(
            connectorType,
            ResourceTypeUtil.getResourceNamespace(badResourceType),
            "test connector");
    badConnector.configure(
        badResourceType.getConnectorConfiguration().asPrismContainerValue(), result);

    // WHEN

    badConnector.test(result);

    // THEN
    result.computeStatus("test failed");
    display("Test result (FAILURE EXPECTED)", result);
    AssertJUnit.assertNotNull(result);
    OperationResult connectorConnectionResult = result.getSubresults().get(1);
    AssertJUnit.assertNotNull(connectorConnectionResult);
    System.out.println(
        "Test \"connector connection\" result: "
            + connectorConnectionResult
            + " (FAILURE EXPECTED)");
    AssertJUnit.assertTrue(
        "Unexpected success of bad connector test", !connectorConnectionResult.isSuccess());
    AssertJUnit.assertTrue(!result.isSuccess());
  }

  /**
   * Test fetching and translating resource schema.
   *
   * @throws Exception
   */
  @Test
  public void test400FetchResourceSchema() throws Exception {
    final String TEST_NAME = "test400FetchResourceSchema";
    TestUtil.displayTestTile(this, TEST_NAME);
    // GIVEN

    // WHEN

    // The schema was fetched during test init. Now just check if it was OK.

    // THEN

    AssertJUnit.assertNotNull(resourceSchema);

    System.out.println(resourceSchema.debugDump());

    Document xsdSchema = resourceSchema.serializeToXsd();

    System.out.println(
        "-------------------------------------------------------------------------------------");
    System.out.println(DOMUtil.printDom(xsdSchema));
    System.out.println(
        "-------------------------------------------------------------------------------------");

    ObjectClassComplexTypeDefinition accountDefinition =
        resourceSchema.findObjectClassDefinition(
            new QName(
                ResourceTypeUtil.getResourceNamespace(resourceType),
                ProvisioningTestUtil.OBJECT_CLASS_INETORGPERSON_NAME));
    AssertJUnit.assertNotNull(accountDefinition);

    AssertJUnit.assertFalse(
        "No identifiers for account object class ", accountDefinition.getIdentifiers().isEmpty());

    PrismPropertyDefinition uidDefinition =
        accountDefinition.findAttributeDefinition(
            new QName(
                ResourceTypeUtil.getResourceNamespace(resourceType),
                ProvisioningTestUtil.RESOURCE_OPENDJ_PRIMARY_IDENTIFIER_LOCAL_NAME));
    AssertJUnit.assertNotNull(uidDefinition);

    for (Definition def : resourceSchema.getDefinitions()) {
      if (def instanceof ResourceAttributeContainerDefinition) {
        ResourceAttributeContainerDefinition rdef = (ResourceAttributeContainerDefinition) def;
        assertNotEmpty("No type name in object class", rdef.getTypeName());
        assertNotEmpty(
            "No native object class for " + rdef.getTypeName(), rdef.getNativeObjectClass());

        // This is maybe not that important, but just for a sake of
        // completeness
        assertNotEmpty("No name for " + rdef.getTypeName(), rdef.getName());
      }
    }
  }

  @Test
  public void test410Capabilities() throws Exception {
    final String TEST_NAME = "test410Capabilities";
    TestUtil.displayTestTile(this, TEST_NAME);
    // GIVEN

    OperationResult result = new OperationResult(TEST_NAME);

    // WHEN

    Collection<Object> capabilities = cc.fetchCapabilities(result);

    // THEN
    result.computeStatus("getCapabilities failed");
    TestUtil.assertSuccess("getCapabilities failed (result)", result);
    assertFalse("Empty capabilities returned", capabilities.isEmpty());
    CredentialsCapabilityType capCred =
        CapabilityUtil.getCapability(capabilities, CredentialsCapabilityType.class);
    assertNotNull("password capability not present", capCred.getPassword());

    PagedSearchCapabilityType capPage =
        CapabilityUtil.getCapability(capabilities, PagedSearchCapabilityType.class);
    assertNotNull("paged search capability not present", capPage);
  }

  @Test
  public void test500FetchObject() throws Exception {
    final String TEST_NAME = "test500FetchObject";
    TestUtil.displayTestTile(this, TEST_NAME);

    // GIVEN
    ResourceAttributeContainer resourceObject =
        createResourceObject("uid=Teell,ou=People,dc=example,dc=com", "Teell William", "Teell");

    OperationResult addResult = new OperationResult(this.getClass().getName() + "." + TEST_NAME);

    PrismObject<ShadowType> shadow = wrapInShadow(ShadowType.class, resourceObject);
    // Add a testing object
    cc.addObject(shadow, null, addResult);

    ObjectClassComplexTypeDefinition accountDefinition =
        resourceObject.getDefinition().getComplexTypeDefinition();

    Collection<ResourceAttribute<?>> identifiers = resourceObject.getIdentifiers();
    // Determine object class from the schema

    ResourceObjectIdentification identification =
        new ResourceObjectIdentification(accountDefinition, identifiers);
    OperationResult result = new OperationResult(this.getClass().getName() + "." + TEST_NAME);

    // WHEN
    PrismObject<ShadowType> ro = cc.fetchObject(ShadowType.class, identification, null, result);

    // THEN

    AssertJUnit.assertNotNull(ro);
    System.out.println("Fetched object " + ro);
    System.out.println("Result:");
    System.out.println(result.debugDump());
  }

  @Test
  public void test510Search() throws Exception {
    final String TEST_NAME = "test510Search";
    TestUtil.displayTestTile(this, TEST_NAME);
    // GIVEN

    ObjectClassComplexTypeDefinition accountDefinition =
        resourceSchema.findObjectClassDefinition(
            ProvisioningTestUtil.OBJECT_CLASS_INETORGPERSON_NAME);
    // Determine object class from the schema

    ResultHandler<ShadowType> handler =
        new ResultHandler<ShadowType>() {
          @Override
          public boolean handle(PrismObject<ShadowType> object) {
            System.out.println("Search: found: " + object);
            return true;
          }
        };

    OperationResult result = new OperationResult(this.getClass().getName() + "." + TEST_NAME);

    // WHEN
    cc.search(accountDefinition, new ObjectQuery(), handler, null, null, null, result);

    // THEN

  }

  @Test
  public void test600CreateAccountWithPassword() throws Exception {
    final String TEST_NAME = "test600CreateAccountWithPassword";
    TestUtil.displayTestTile(this, TEST_NAME);
    // GIVEN
    ResourceAttributeContainer resourceObject =
        createResourceObject(
            "uid=lechuck,ou=people,dc=example,dc=com", "Ghost Pirate LeChuck", "LeChuck");

    Set<Operation> additionalOperations = new HashSet<Operation>();
    ProtectedStringType ps = protector.encryptString("t4k30v3rTh3W0rld");

    //		PasswordChangeOperation passOp = new PasswordChangeOperation(ps);
    //		additionalOperations.add(passOp);

    OperationResult addResult = new OperationResult(this.getClass().getName() + "." + TEST_NAME);

    PrismObject<ShadowType> shadow = wrapInShadow(ShadowType.class, resourceObject);
    CredentialsType credentials = new CredentialsType();
    PasswordType pass = new PasswordType();
    pass.setValue(ps);
    credentials.setPassword(pass);
    shadow.asObjectable().setCredentials(credentials);

    // WHEN
    cc.addObject(shadow, additionalOperations, addResult);

    // THEN

    String entryUuid = (String) resourceObject.getIdentifier().getValue().getValue();
    SearchResultEntry entry = openDJController.searchAndAssertByEntryUuid(entryUuid);
    display("Entry before change", entry);
    String passwordAfter = OpenDJController.getAttributeValue(entry, "userPassword");

    assertNotNull(passwordAfter);

    System.out.println("Changed password: "******"test610ChangePassword";
    TestUtil.displayTestTile(this, TEST_NAME);
    // GIVEN
    ResourceAttributeContainer resourceObject =
        createResourceObject("uid=drake,ou=People,dc=example,dc=com", "Sir Francis Drake", "Drake");
    PrismObject<ShadowType> shadow = wrapInShadow(ShadowType.class, resourceObject);

    OperationResult addResult = new OperationResult(this.getClass().getName() + "." + TEST_NAME);

    // Add a testing object
    cc.addObject(shadow, null, addResult);

    String entryUuid = (String) resourceObject.getIdentifier().getValue().getValue();
    SearchResultEntry entry = openDJController.searchAndAssertByEntryUuid(entryUuid);
    display("Entry before change", entry);
    String passwordBefore = OpenDJController.getAttributeValue(entry, "userPassword");
    // We have set no password during create, therefore the password should
    // be empty
    assertNull(passwordBefore);

    ObjectClassComplexTypeDefinition accountDefinition =
        resourceObject.getDefinition().getComplexTypeDefinition();

    Collection<ResourceAttribute<?>> identifiers = resourceObject.getIdentifiers();
    // Determine object class from the schema

    OperationResult result = new OperationResult(this.getClass().getName() + ".testFetchObject");

    // WHEN

    Set<Operation> changes = new HashSet<Operation>();
    ProtectedStringType passPs = protector.encryptString("salalala");

    ItemDeltaType propMod = new ItemDeltaType();
    // create modification path
    Document doc = DOMUtil.getDocument();
    ItemPathType path = new ItemPathType("credentials/password/value");
    //		PropertyPath propPath = new PropertyPath(new
    // PropertyPath(ResourceObjectShadowType.F_CREDENTIALS), CredentialsType.F_PASSWORD);
    propMod.setPath(path);

    // set the replace value
    MapXNode passPsXnode = prismContext.getBeanConverter().marshalProtectedDataType(passPs);
    RawType value = new RawType(passPsXnode, prismContext);
    propMod.getValue().add(value);

    // set the modificaion type
    propMod.setModificationType(ModificationTypeType.REPLACE);

    PropertyDelta passDelta =
        (PropertyDelta) DeltaConvertor.createItemDelta(propMod, shadow.getDefinition());
    PropertyModificationOperation passwordModification =
        new PropertyModificationOperation(passDelta);
    changes.add(passwordModification);

    //		PasswordChangeOperation passwordChange = new PasswordChangeOperation(passPs);
    //		changes.add(passwordChange);
    cc.modifyObject(accountDefinition, identifiers, changes, result);

    // THEN

    entry = openDJController.searchAndAssertByEntryUuid(entryUuid);
    display("Entry after change", entry);

    String passwordAfter = OpenDJController.getAttributeValue(entry, "userPassword");
    assertNotNull(passwordAfter);

    System.out.println("Account password: "******"sn"));
    ResourceAttribute roa = road.instantiate();
    roa.setValue(new PrismPropertyValue(sn));
    resourceObject.add(roa);

    road =
        accountDefinition.findAttributeDefinition(
            new QName(ResourceTypeUtil.getResourceNamespace(resourceType), "cn"));
    roa = road.instantiate();
    roa.setValue(new PrismPropertyValue(cn));
    resourceObject.add(roa);

    road =
        accountDefinition.findAttributeDefinition(
            new QName(
                ResourceTypeUtil.getResourceNamespace(resourceType),
                ProvisioningTestUtil.RESOURCE_OPENDJ_SECONDARY_IDENTIFIER_LOCAL_NAME));
    roa = road.instantiate();
    roa.setValue(new PrismPropertyValue(dn));
    resourceObject.add(roa);

    return resourceObject;
  }

  private <T extends ShadowType> PrismObject<T> wrapInShadow(
      Class<T> type, ResourceAttributeContainer resourceObject) throws SchemaException {
    PrismObjectDefinition<T> shadowDefinition = getShadowDefinition(type);
    PrismObject<T> shadow = shadowDefinition.instantiate();
    resourceObject.setElementName(ShadowType.F_ATTRIBUTES);
    shadow.getValue().add(resourceObject);
    return shadow;
  }

  private <T extends ShadowType> PrismObjectDefinition<T> getShadowDefinition(Class<T> type) {
    return prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(type);
  }
}
/** @author semancik */
@Component
public class ModelDiagController implements ModelDiagnosticService {

  public static final String CLASS_NAME_WITH_DOT = ModelDiagController.class.getName() + ".";
  private static final String REPOSITORY_SELF_TEST_USER =
      CLASS_NAME_WITH_DOT + "repositorySelfTest.user";

  private static final String NAME_PREFIX = "selftest";
  private static final int NAME_RANDOM_LENGTH = 5;

  private static final String USER_FULL_NAME = "Grăfula Fèlix Teleke z Tölökö";
  private static final String USER_GIVEN_NAME = "Fëľïx";
  private static final String USER_FAMILY_NAME = "Ţæĺêké";
  private static final String[] USER_ORGANIZATION = {
    "COMITATVS NOBILITVS HVNGARIÆ", "Salsa Verde ğomorula prïvatûła"
  };
  private static final String[] USER_EMPLOYEE_TYPE = {"Ģŗąfųŀą", "CANTATOR"};

  private static final String INSANE_NATIONAL_STRING = "Pørúga ném nå väšȍm apârátula";

  private static final Trace LOGGER = TraceManager.getTrace(ModelDiagController.class);

  @Autowired(required = true)
  private PrismContext prismContext;

  @Autowired(required = true)
  @Qualifier("repositoryService")
  private transient RepositoryService repositoryService;

  @Autowired(required = true)
  private ProvisioningService provisioningService;

  private RandomString randomString;

  ModelDiagController() {
    randomString = new RandomString(NAME_RANDOM_LENGTH, true);
  }

  /* (non-Javadoc)
   * @see com.evolveum.midpoint.model.api.ModelDiagnosticService#getRepositoryDiag(com.evolveum.midpoint.task.api.Task, com.evolveum.midpoint.schema.result.OperationResult)
   */
  @Override
  public RepositoryDiag getRepositoryDiag(Task task, OperationResult parentResult) {
    return repositoryService.getRepositoryDiag();
  }

  /* (non-Javadoc)
   * @see com.evolveum.midpoint.model.api.ModelDiagnosticService#repositorySelfTest(com.evolveum.midpoint.task.api.Task)
   */
  @Override
  public OperationResult repositorySelfTest(Task task) {
    OperationResult testResult = new OperationResult(REPOSITORY_SELF_TEST);
    // Give repository chance to run its own self-test if available
    repositoryService.repositorySelfTest(testResult);

    repositorySelfTestUser(task, testResult);

    testResult.computeStatus();
    return testResult;
  }

  @Override
  public OperationResult provisioningSelfTest(Task task) {
    OperationResult testResult = new OperationResult(PROVISIONING_SELF_TEST);
    // Give provisioning chance to run its own self-test
    provisioningService.provisioningSelfTest(testResult, task);

    testResult.computeStatus();
    return testResult;
  }

  private void repositorySelfTestUser(Task task, OperationResult testResult) {
    OperationResult result = testResult.createSubresult(REPOSITORY_SELF_TEST_USER);

    PrismObject<UserType> user = getObjectDefinition(UserType.class).instantiate();
    UserType userType = user.asObjectable();

    String name = generateRandomName();
    PolyStringType namePolyStringType = toPolyStringType(name);
    userType.setName(namePolyStringType);
    result.addContext("name", name);
    userType.setDescription(SelfTestData.POLICIJA);
    userType.setFullName(toPolyStringType(USER_FULL_NAME));
    userType.setGivenName(toPolyStringType(USER_GIVEN_NAME));
    userType.setFamilyName(toPolyStringType(USER_FAMILY_NAME));
    userType.setTitle(toPolyStringType(INSANE_NATIONAL_STRING));
    userType.getEmployeeType().add(USER_EMPLOYEE_TYPE[0]);
    userType.getEmployeeType().add(USER_EMPLOYEE_TYPE[1]);
    userType.getOrganization().add(toPolyStringType(USER_ORGANIZATION[0]));
    userType.getOrganization().add(toPolyStringType(USER_ORGANIZATION[1]));

    String oid;
    try {
      oid = repositoryService.addObject(user, null, result);
    } catch (ObjectAlreadyExistsException e) {
      result.recordFatalError(e);
      return;
    } catch (SchemaException e) {
      result.recordFatalError(e);
      return;
    } catch (RuntimeException e) {
      result.recordFatalError(e);
      return;
    }

    try {

      {
        OperationResult subresult = result.createSubresult(result.getOperation() + ".getObject");

        PrismObject<UserType> userRetrieved;
        try {
          userRetrieved = repositoryService.getObject(UserType.class, oid, null, subresult);
        } catch (ObjectNotFoundException e) {
          result.recordFatalError(e);
          return;
        } catch (SchemaException e) {
          result.recordFatalError(e);
          return;
        } catch (RuntimeException e) {
          result.recordFatalError(e);
          return;
        }

        if (LOGGER.isTraceEnabled()) {
          LOGGER.trace("Self-test:user getObject:\n{}", userRetrieved.debugDump());
        }

        checkUser(userRetrieved, name, subresult);

        subresult.recordSuccessIfUnknown();
      }

      {
        OperationResult subresult =
            result.createSubresult(result.getOperation() + ".searchObjects.fullName");
        try {

          ObjectQuery query = new ObjectQuery();
          ObjectFilter filter =
              EqualFilter.createEqual(
                  UserType.F_FULL_NAME,
                  UserType.class,
                  prismContext,
                  null,
                  toPolyString(USER_FULL_NAME));
          query.setFilter(filter);
          subresult.addParam("query", query);
          List<PrismObject<UserType>> foundObjects =
              repositoryService.searchObjects(UserType.class, query, null, subresult);
          if (LOGGER.isTraceEnabled()) {
            LOGGER.trace("Self-test:user searchObjects:\n{}", DebugUtil.debugDump(foundObjects));
          }
          assertSingleSearchResult("user", foundObjects, subresult);

          PrismObject<UserType> userRetrieved = foundObjects.iterator().next();
          checkUser(userRetrieved, name, subresult);

          subresult.recordSuccessIfUnknown();
        } catch (SchemaException e) {
          subresult.recordFatalError(e);
          return;
        } catch (RuntimeException e) {
          subresult.recordFatalError(e);
          return;
        }
      }

      // MID-1116
      {
        OperationResult subresult =
            result.createSubresult(result.getOperation() + ".searchObjects.employeeType");
        try {

          ObjectQuery query = new ObjectQuery();
          ObjectFilter filter =
              EqualFilter.createEqual(
                  UserType.F_EMPLOYEE_TYPE,
                  UserType.class,
                  prismContext,
                  null,
                  USER_EMPLOYEE_TYPE[0]);
          query.setFilter(filter);
          subresult.addParam("query", query);
          List<PrismObject<UserType>> foundObjects =
              repositoryService.searchObjects(UserType.class, query, null, subresult);
          if (LOGGER.isTraceEnabled()) {
            LOGGER.trace("Self-test:user searchObjects:\n{}", DebugUtil.debugDump(foundObjects));
          }
          assertSingleSearchResult("user", foundObjects, subresult);

          PrismObject<UserType> userRetrieved = foundObjects.iterator().next();
          checkUser(userRetrieved, name, subresult);

          subresult.recordSuccessIfUnknown();
        } catch (SchemaException e) {
          subresult.recordFatalError(e);
          return;
        } catch (RuntimeException e) {
          subresult.recordFatalError(e);
          return;
        }
      }

      // MID-1116
      {
        OperationResult subresult =
            result.createSubresult(result.getOperation() + ".searchObjects.organization");
        try {

          ObjectQuery query = new ObjectQuery();
          ObjectFilter filter =
              EqualFilter.createEqual(
                  UserType.F_ORGANIZATION,
                  UserType.class,
                  prismContext,
                  null,
                  toPolyString(USER_ORGANIZATION[1]));
          query.setFilter(filter);
          subresult.addParam("query", query);
          List<PrismObject<UserType>> foundObjects =
              repositoryService.searchObjects(UserType.class, query, null, subresult);
          if (LOGGER.isTraceEnabled()) {
            LOGGER.trace("Self-test:user searchObjects:\n{}", DebugUtil.debugDump(foundObjects));
          }
          assertSingleSearchResult("user", foundObjects, subresult);

          PrismObject<UserType> userRetrieved = foundObjects.iterator().next();
          checkUser(userRetrieved, name, subresult);

          subresult.recordSuccessIfUnknown();
        } catch (SchemaException e) {
          subresult.recordFatalError(e);
          return;
        } catch (RuntimeException e) {
          subresult.recordFatalError(e);
          return;
        }
      }

    } finally {

      try {
        repositoryService.deleteObject(UserType.class, oid, testResult);
      } catch (ObjectNotFoundException e) {
        result.recordFatalError(e);
        return;
      } catch (RuntimeException e) {
        result.recordFatalError(e);
        return;
      }

      result.computeStatus();
    }
  }

  private void checkUser(
      PrismObject<UserType> userRetrieved, String name, OperationResult subresult) {
    checkUserPropertyPolyString(userRetrieved, UserType.F_NAME, subresult, name);
    checkUserProperty(userRetrieved, UserType.F_DESCRIPTION, subresult, SelfTestData.POLICIJA);
    checkUserPropertyPolyString(userRetrieved, UserType.F_FULL_NAME, subresult, USER_FULL_NAME);
    checkUserPropertyPolyString(userRetrieved, UserType.F_GIVEN_NAME, subresult, USER_GIVEN_NAME);
    checkUserPropertyPolyString(userRetrieved, UserType.F_FAMILY_NAME, subresult, USER_FAMILY_NAME);
    checkUserPropertyPolyString(userRetrieved, UserType.F_TITLE, subresult, INSANE_NATIONAL_STRING);
    checkUserProperty(userRetrieved, UserType.F_EMPLOYEE_TYPE, subresult, USER_EMPLOYEE_TYPE);
    checkUserPropertyPolyString(
        userRetrieved, UserType.F_ORGANIZATION, subresult, USER_ORGANIZATION);
  }

  private void assertSingleSearchResult(
      String objectTypeMessage,
      List<PrismObject<UserType>> foundObjects,
      OperationResult parentResult) {
    OperationResult result =
        parentResult.createSubresult(parentResult.getOperation() + ".numberOfResults");
    assertTrue("Found no " + objectTypeMessage, !foundObjects.isEmpty(), result);
    assertTrue(
        "Expected to find a single " + objectTypeMessage + " but found " + foundObjects.size(),
        foundObjects.size() == 1,
        result);
    result.recordSuccessIfUnknown();
  }

  private <O extends ObjectType, T> void checkUserProperty(
      PrismObject<O> object, QName propQName, OperationResult parentResult, T... expectedValues) {
    String propName = propQName.getLocalPart();
    OperationResult result =
        parentResult.createSubresult(parentResult.getOperation() + "." + propName);
    PrismProperty<T> prop = object.findProperty(propQName);
    Collection<T> actualValues = prop.getRealValues();
    result.addArbitraryCollectionAsParam("actualValues", actualValues);
    assertMultivalue("User, property '" + propName + "'", expectedValues, actualValues, result);
    result.recordSuccessIfUnknown();
  }

  private <T> void assertMultivalue(
      String message, T expectedVals[], Collection<T> actualVals, OperationResult result) {
    if (expectedVals.length != actualVals.size()) {
      fail(
          message
              + ": expected "
              + expectedVals.length
              + " values but has "
              + actualVals.size()
              + " values: "
              + actualVals,
          result);
      return;
    }
    for (T expected : expectedVals) {
      boolean found = false;
      for (T actual : actualVals) {
        if (expected.equals(actual)) {
          found = true;
          break;
        }
      }
      if (!found) {
        fail(
            message
                + ": expected value '"
                + expected
                + "' not found in actual values "
                + actualVals,
            result);
        return;
      }
    }
  }

  private <O extends ObjectType> void checkUserPropertyPolyString(
      PrismObject<O> object,
      QName propQName,
      OperationResult parentResult,
      String... expectedValues) {
    String propName = propQName.getLocalPart();
    OperationResult result =
        parentResult.createSubresult(parentResult.getOperation() + "." + propName);
    PrismProperty<PolyString> prop = object.findProperty(propQName);
    Collection<PolyString> actualValues = prop.getRealValues();
    result.addArbitraryCollectionAsParam("actualValues", actualValues);
    assertMultivaluePolyString(
        "User, property '" + propName + "'", expectedValues, actualValues, result);
    result.recordSuccessIfUnknown();
  }

  private void assertMultivaluePolyString(
      String message,
      String expectedOrigs[],
      Collection<PolyString> actualPolyStrings,
      OperationResult result) {
    if (expectedOrigs.length != actualPolyStrings.size()) {
      fail(
          message
              + ": expected "
              + expectedOrigs.length
              + " values but has "
              + actualPolyStrings.size()
              + " values: "
              + actualPolyStrings,
          result);
      return;
    }
    for (String expectedOrig : expectedOrigs) {
      boolean found = false;
      for (PolyString actualPolyString : actualPolyStrings) {
        if (expectedOrig.equals(actualPolyString.getOrig())) {
          found = true;
          assertEquals(
              message + ", norm", polyStringNorm(expectedOrig), actualPolyString.getNorm(), result);
          break;
        }
      }
      if (!found) {
        fail(
            message
                + ": expected value '"
                + expectedOrig
                + "' not found in actual values "
                + actualPolyStrings,
            result);
        return;
      }
    }
  }

  private void assertPolyString(
      String message, String expectedOrig, PolyString actualPolyString, OperationResult result) {
    assertEquals(message + ", orig", expectedOrig, actualPolyString.getOrig(), result);
    assertEquals(
        message + ", norm", polyStringNorm(expectedOrig), actualPolyString.getNorm(), result);
  }

  private void assertPolyStringType(
      String message,
      String expectedName,
      PolyStringType actualPolyStringType,
      OperationResult result) {
    assertEquals(message + ", orig", expectedName, actualPolyStringType.getOrig(), result);
    assertEquals(
        message + ", norm", polyStringNorm(expectedName), actualPolyStringType.getNorm(), result);
  }

  private String polyStringNorm(String orig) {
    return prismContext.getDefaultPolyStringNormalizer().normalize(orig);
  }

  private void assertTrue(String message, boolean condition, OperationResult result) {
    if (!condition) {
      fail(message, result);
    }
  }

  private void assertEquals(
      String message, Object expected, Object actual, OperationResult result) {
    if (!MiscUtil.equals(expected, actual)) {
      fail(message + "; expected " + expected + ", actual " + actual, result);
    }
  }

  private void fail(String message, OperationResult result) {
    result.recordFatalError(message);
    LOGGER.error("Repository self-test assertion failed: {}", message);
  }

  private String generateRandomName() {
    return NAME_PREFIX + randomString.nextString();
  }

  private PolyStringType toPolyStringType(String orig) {
    PolyStringType polyStringType = new PolyStringType();
    polyStringType.setOrig(orig);
    return polyStringType;
  }

  private PolyString toPolyString(String orig) {
    PolyString polyString = new PolyString(orig);
    polyString.recompute(prismContext.getDefaultPolyStringNormalizer());
    return polyString;
  }

  private <T extends ObjectType> PrismObjectDefinition<T> getObjectDefinition(Class<T> type) {
    return prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(type);
  }
}
Example #9
0
public class ReportFunctions {

  private static final Trace LOGGER = TraceManager.getTrace(ReportFunctions.class);

  private PrismContext prismContext;
  private ModelService model;

  private TaskManager taskManager;

  private AuditService auditService;

  public ReportFunctions(
      PrismContext prismContext,
      ModelService modelService,
      TaskManager taskManager,
      AuditService auditService) {
    this.prismContext = prismContext;
    this.model = modelService;
    this.taskManager = taskManager;
    this.auditService = auditService;
  }

  public <O extends ObjectType> O resolveObject(ObjectReferenceType ref) {
    Validate.notNull(ref.getOid(), "Object oid must not be null");
    Validate.notNull(ref.getType(), "Object type must not be null");

    Class type = prismContext.getSchemaRegistry().determineCompileTimeClass(ref.getType());
    return resolveObject(type, ref.getOid());
  }

  public <O extends ObjectType> O resolveObject(Class type, String oid) {
    Task task = taskManager.createTaskInstance();
    OperationResult parentResult = task.getResult();
    PrismObject<O> obj;
    try {
      obj =
          model.getObject(
              type,
              oid,
              SelectorOptions.createCollection(GetOperationOptions.createResolveNames()),
              task,
              parentResult);
      return obj.asObjectable();
    } catch (ObjectNotFoundException
        | SchemaException
        | SecurityViolationException
        | CommunicationException
        | ConfigurationException e) {
      // TODO Auto-generated catch block
      LOGGER.error("Could not get object with oid " + oid + ". Reason: " + e.getMessage());
    }
    return null;
  }

  public <O extends ObjectType> List<PrismObject<O>> resolveLinkRefs(
      Collection<ObjectReferenceType> refs, Class type) {

    List<PrismObject<O>> objects = new ArrayList<>();

    for (ObjectReferenceType ref : refs) {
      Class clazz = getClassForType(ref.getType());
      if (!clazz.equals(type)) {
        continue;
      }
      Task task = taskManager.createTaskInstance();
      OperationResult parentResult = task.getResult();
      try {
        PrismObject<O> obj =
            model.getObject(
                type,
                ref.getOid(),
                SelectorOptions.createCollection(GetOperationOptions.createResolveNames()),
                task,
                parentResult);
        objects.add(obj);
      } catch (ObjectNotFoundException
          | SchemaException
          | SecurityViolationException
          | CommunicationException
          | ConfigurationException e) {
        // TODO Auto-generated catch block
        LOGGER.error(
            "Could not get object with oid " + ref.getOid() + ". Reason: " + e.getMessage());
      }
    }
    return objects;
  }

  public String resolveRefName(ObjectReferenceType ref) {
    if (ref == null) {
      return null;
    }
    PrismReferenceValue refValue = ref.asReferenceValue();
    Object name = refValue.getTargetName() != null ? ref.getTargetName().getOrig() : null;
    if (!(name instanceof String)) {
      LOGGER.error("Couldn't resolve object name");
    }

    return (String) name;
  }

  public List<PrismObject<RoleType>> resolveRoles(Collection<AssignmentType> assignments) {
    return resolveAssignments(assignments, RoleType.class);
  }

  public List<PrismObject<OrgType>> resolveOrgs(Collection<AssignmentType> assignments) {
    return resolveAssignments(assignments, OrgType.class);
  }

  public List<PrismObject<RoleType>> resolveRoles(AssignmentType assignments) {
    return resolveAssignments(assignments, RoleType.class);
  }

  public List<PrismObject<OrgType>> resolveOrgs(AssignmentType assignments) {
    return resolveAssignments(assignments, OrgType.class);
  }

  public <O extends ObjectType> List<PrismObject<O>> resolveAssignments(
      AssignmentType assignment, Class<O> type) {
    List<AssignmentType> assignments = new ArrayList<>();
    assignments.add(assignment);
    return resolveAssignments(assignments, type);
  }

  public <O extends ObjectType> List<PrismObject<O>> resolveAssignments(
      Collection<AssignmentType> assignments, Class<O> type) {
    List<PrismObject<O>> resolvedAssignments = new ArrayList<>();
    if (assignments == null) {
      return resolvedAssignments;
    }
    for (AssignmentType assignment : assignments) {
      Class clazz = null;
      String oid = null;
      if (assignment.getTargetRef() != null) {
        clazz = getClassForType(assignment.getTargetRef().getType());
        oid = assignment.getTargetRef().getOid();
      } else if (assignment.getTarget() != null) {
        clazz = assignment.getTarget().getClass();
      } else if (assignment.getTenantRef() != null) {
        clazz = getClassForType(assignment.getTenantRef().getType());
        oid = assignment.getTenantRef().getOid();
      }

      if (clazz == null && assignment.getConstruction() != null) {
        continue;
      } else {
        LOGGER.debug("Could not resolve assignment for type {}. No target type defined.", type);
      }

      if (!clazz.equals(type)) {
        continue;
      }

      if (assignment.getTarget() != null) {
        resolvedAssignments.add(assignment.getTarget().asPrismObject());
        continue;
      }

      Task task = taskManager.createTaskInstance();
      try {
        PrismObject<O> obj = model.getObject(type, oid, null, task, task.getResult());
        resolvedAssignments.add(obj);
      } catch (ObjectNotFoundException
          | SchemaException
          | SecurityViolationException
          | CommunicationException
          | ConfigurationException e) {
        LOGGER.error("Could not get object with oid " + oid + ". Reason: " + e.getMessage());
      }
    }

    return resolvedAssignments;
  }

  public List<AuditEventRecord> searchAuditRecords(String query, Map<String, Object> params) {

    if (StringUtils.isBlank(query)) {
      return new ArrayList<>();
    }

    Map<String, Object> resultSet = new HashMap<String, Object>();
    Set<Entry<String, Object>> paramSet = params.entrySet();
    for (Entry<String, Object> p : paramSet) {
      if (p.getValue() instanceof AuditEventTypeType) {
        resultSet.put(
            p.getKey(), AuditEventType.toAuditEventType((AuditEventTypeType) p.getValue()));
      } else if (p.getValue() instanceof AuditEventStageType) {
        resultSet.put(
            p.getKey(), AuditEventStage.toAuditEventStage((AuditEventStageType) p.getValue()));
      } else {
        resultSet.put(p.getKey(), p.getValue());
      }
    }
    return auditService.listRecords(query, resultSet);
  }

  public UserType getShadowOwner(String shadowOid) {
    Task task = taskManager.createTaskInstance();
    try {
      PrismObject<UserType> owner = model.findShadowOwner(shadowOid, task, task.getResult());
      return owner.asObjectable();
    } catch (ObjectNotFoundException
        | SecurityViolationException
        | SchemaException
        | ConfigurationException e) {
      // TODO Auto-generated catch block
      LOGGER.error(
          "Could not find owner for shadow with oid " + shadowOid + ". Reason: " + e.getMessage());
    }

    return null;
  }

  private Class getClassForType(QName type) {
    return prismContext.getSchemaRegistry().determineCompileTimeClass(type);
  }

  <T extends ObjectType> List<T> searchObjects(Class<T> type, ObjectQuery query) {
    List<T> ret = new ArrayList();
    Task task = taskManager.createTaskInstance();
    try {
      List<PrismObject<T>> list =
          model.searchObjects(type, query, null, task, task.getResult()).getList();
      for (PrismObject<T> po : list) {
        ret.add(po.asObjectable());
      }
    } catch (SchemaException
        | ObjectNotFoundException
        | SecurityViolationException
        | CommunicationException
        | ConfigurationException e) {
      LOGGER.error(
          "Could not search objects of type: "
              + type
              + " with query "
              + query
              + ". Reason: "
              + e.getMessage());
    }
    return ret;
  }

  <C extends Containerable, T> EqualFilter<T> createEqualFilter(
      QName propertyName, Class<C> type, T realValues) throws SchemaException {
    return EqualFilter.createEqual(propertyName, type, prismContext, realValues);
  }

  <C extends Containerable, T> EqualFilter<T> createEqualFilter(
      ItemPath propertyPath, Class<C> type, T realValues) throws SchemaException {
    return EqualFilter.createEqual(propertyPath, type, prismContext, realValues);
  }

  <O extends Containerable> RefFilter createReferenceEqualFilter(
      QName propertyName, Class<O> type, String... oids) {
    return RefFilter.createReferenceEqual(propertyName, type, prismContext, oids);
  }

  <O extends Containerable> RefFilter createReferenceEqualFilter(
      ItemPath propertyPath, Class<O> type, String... oids) throws SchemaException {
    return RefFilter.createReferenceEqual(propertyPath, type, prismContext, oids);
  }

  Object parseObjectFromXML(String xml) throws SchemaException {
    return prismContext.parseAnyData(xml, PrismContext.LANG_XML);
  }

  /**
   * Retrieves all definitions. Augments them by count of campaigns (all + open ones).
   *
   * <p>TODO query parameters, customizable sorting definitions and campaigns counts are expected to
   * be low, so we can afford to go through all of them here
   */
  public Collection<PrismObject<AccessCertificationDefinitionForReportType>>
      searchCertificationDefinitions()
          throws ConfigurationException, SchemaException, ObjectNotFoundException,
              CommunicationException, SecurityViolationException {

    Task task = taskManager.createTaskInstance();
    OperationResult result = task.getResult();
    Collection<SelectorOptions<GetOperationOptions>> options =
        SelectorOptions.createCollection(GetOperationOptions.createResolveNames());
    List<PrismObject<AccessCertificationDefinitionType>> definitions =
        model.searchObjects(AccessCertificationDefinitionType.class, null, options, task, result);
    final Map<String, PrismObject<AccessCertificationDefinitionForReportType>>
        definitionsForReportMap = new HashMap<>();
    for (PrismObject<AccessCertificationDefinitionType> definition : definitions) {
      // create subclass with the values copied from the superclass
      PrismObject<AccessCertificationDefinitionForReportType> definitionForReport =
          prismContext
              .createObjectable(AccessCertificationDefinitionForReportType.class)
              .asPrismObject();
      for (Item<?, ?> item : definition.getValue().getItems()) {
        definitionForReport.getValue().add(item.clone());
      }
      definitionsForReportMap.put(definition.getOid(), definitionForReport);
    }

    ResultHandler<AccessCertificationCampaignType> handler =
        new ResultHandler<AccessCertificationCampaignType>() {
          @Override
          public boolean handle(
              PrismObject<AccessCertificationCampaignType> campaignObject,
              OperationResult parentResult) {
            AccessCertificationCampaignType campaign = campaignObject.asObjectable();
            if (campaign.getDefinitionRef() != null) {
              String definitionOid = campaign.getDefinitionRef().getOid();
              PrismObject<AccessCertificationDefinitionForReportType> definitionObject =
                  definitionsForReportMap.get(definitionOid);
              if (definitionObject != null) {
                AccessCertificationDefinitionForReportType definition =
                    definitionObject.asObjectable();
                int campaigns = definition.getCampaigns() != null ? definition.getCampaigns() : 0;
                definition.setCampaigns(campaigns + 1);
                AccessCertificationCampaignStateType state = campaign.getState();
                if (state != AccessCertificationCampaignStateType.CREATED && state != CLOSED) {
                  int openCampaigns =
                      definition.getOpenCampaigns() != null ? definition.getOpenCampaigns() : 0;
                  definition.setOpenCampaigns(openCampaigns + 1);
                }
              }
            }
            return true;
          }
        };
    model.searchObjectsIterative(
        AccessCertificationCampaignType.class, null, handler, null, task, result);

    List<PrismObject<AccessCertificationDefinitionForReportType>> rv =
        new ArrayList<>(definitionsForReportMap.values());
    Collections.sort(
        rv,
        new Comparator<PrismObject<AccessCertificationDefinitionForReportType>>() {
          @Override
          public int compare(
              PrismObject<AccessCertificationDefinitionForReportType> o1,
              PrismObject<AccessCertificationDefinitionForReportType> o2) {
            String n1 = o1.asObjectable().getName().getOrig();
            String n2 = o2.asObjectable().getName().getOrig();
            if (n1 == null) {
              n1 = "";
            }
            return n1.compareTo(n2);
          }
        });
    for (PrismObject<AccessCertificationDefinitionForReportType> defObject : rv) {
      AccessCertificationDefinitionForReportType def = defObject.asObjectable();
      if (def.getCampaigns() == null) {
        def.setCampaigns(0);
      }
      if (def.getOpenCampaigns() == null) {
        def.setOpenCampaigns(0);
      }
    }
    return rv;
  }

  public List<PrismContainerValue<AccessCertificationCaseType>> getCertificationCampaignCases(
      String campaignName)
      throws SchemaException, SecurityViolationException, ConfigurationException,
          ObjectNotFoundException {
    List<AccessCertificationCaseType> cases = getCertificationCampaignCasesAsBeans(campaignName);
    return PrismContainerValue.toPcvList(cases);
  }

  private List<AccessCertificationCaseType> getCertificationCampaignCasesAsBeans(
      String campaignName)
      throws SchemaException, SecurityViolationException, ConfigurationException,
          ObjectNotFoundException {
    Task task = taskManager.createTaskInstance();
    ObjectQuery query;
    if (StringUtils.isEmpty(campaignName)) {
      // query = null;
      return new ArrayList<>();
    } else {
      query =
          QueryBuilder.queryFor(AccessCertificationCaseType.class, prismContext)
              .item(PrismConstants.T_PARENT, F_NAME)
              .eqPoly(campaignName, "")
              .matchingOrig()
              .asc(
                  CertCampaignTypeUtil.getOrderBy(
                      F_OBJECT_REF)) // TODO first by type then by name (not supported by the
              // repository as of now)
              .asc(CertCampaignTypeUtil.getOrderBy(F_TARGET_REF)) // the same
              .build();
    }
    Collection<SelectorOptions<GetOperationOptions>> options =
        SelectorOptions.createCollection(GetOperationOptions.createResolveNames());
    return model.searchContainers(
        AccessCertificationCaseType.class, query, options, task, task.getResult());
  }

  public List<PrismContainerValue<AccessCertificationDecisionType>>
      getCertificationCampaignDecisions(String campaignName, Integer stageNumber)
          throws SchemaException, SecurityViolationException, ConfigurationException,
              ObjectNotFoundException {
    List<AccessCertificationCaseType> cases = getCertificationCampaignCasesAsBeans(campaignName);
    List<AccessCertificationDecisionType> decisions = new ArrayList<>();
    for (AccessCertificationCaseType aCase : cases) {
      for (AccessCertificationDecisionType decision : aCase.getDecision()) {
        if (stageNumber == null || decision.getStageNumber() == stageNumber) {
          decisions.add(decision);
        }
      }
    }
    return PrismContainerValue.toPcvList(decisions);
  }

  public List<PrismObject<AccessCertificationCampaignType>> getCertificationCampaigns(
      Boolean alsoClosedCampaigns)
      throws SchemaException, ConfigurationException, ObjectNotFoundException,
          CommunicationException, SecurityViolationException {
    Task task = taskManager.createTaskInstance();

    ObjectQuery query =
        QueryBuilder.queryFor(AccessCertificationCampaignType.class, prismContext)
            .asc(F_NAME)
            .build();
    if (!Boolean.TRUE.equals(alsoClosedCampaigns)) {
      query.addFilter(
          QueryBuilder.queryFor(AccessCertificationCampaignType.class, prismContext)
              .not()
              .item(F_STATE)
              .eq(CLOSED)
              .buildFilter());
    }

    Collection<SelectorOptions<GetOperationOptions>> options =
        SelectorOptions.createCollection(GetOperationOptions.createResolveNames());
    options.add(
        SelectorOptions.create(
            AccessCertificationCampaignType.F_CASE,
            GetOperationOptions.createRetrieve(RetrieveOption.INCLUDE)));

    return model.searchObjects(
        AccessCertificationCampaignType.class, query, options, task, task.getResult());
  }
}
@PageDescriptor(url = "/PasswordQuestions")
public class PageMyPasswordQuestions extends PageAdminHome {

  private static final Trace LOGGER = TraceManager.getTrace(PageMyPasswordQuestions.class);

  private static final String DOT_CLASS = PageMyPasswordQuestions.class.getName() + ".";
  private static final String OPERATION_LOAD_USER = DOT_CLASS + "loaduser";
  private static final String OPERATION_LOAD_QUESTION_POLICY = DOT_CLASS + "LOAD Question Policy";
  private String ID_PASSWORD_QUESTIONS_PANEL = "pwdQuestionsPanel";
  private static final String OPERATION_SAVE_QUESTIONS = "Save Security Questions";

  private static final String ID_MAIN_FORM = "mainForm";
  private static final String ID_BACK = "back";
  private static final String ID_SAVE = "save";
  private LoadableModel<ObjectWrapper> userModel;

  private List<MyPasswordQuestionsPanel> pqPanels;
  private IModel<PasswordQuestionsDto> model;
  private List<SecurityQuestionDefinitionType> policyQuestionList;
  private MyPasswordQuestionsPanel pwPanel;
  int questionNumber;

  public PageMyPasswordQuestions() {

    model =
        new LoadableModel<PasswordQuestionsDto>(false) {

          private static final long serialVersionUID = 1L;

          @Override
          protected PasswordQuestionsDto load() {
            return loadPageModel();
          }
        };

    initLayout();
  }

  public PageMyPasswordQuestions(IModel<PasswordQuestionsDto> model) {
    this.model = model;
    initLayout();
  }

  public PageMyPasswordQuestions(final PrismObject<UserType> userToEdit) {
    userModel =
        new LoadableModel<ObjectWrapper>(false) {

          @Override
          protected ObjectWrapper load() {
            return loadUserWrapper(userToEdit);
          }
        };
    initLayout();
  }

  private PasswordQuestionsDto loadPageModel() {
    LOGGER.debug("Loading user for Security Question Page.");

    PasswordQuestionsDto dto = new PasswordQuestionsDto();
    OperationResult result = new OperationResult(OPERATION_LOAD_USER);
    try {

      String userOid = SecurityUtils.getPrincipalUser().getOid();
      Task task = createSimpleTask(OPERATION_LOAD_USER);
      OperationResult subResult = result.createSubresult(OPERATION_LOAD_USER);

      PrismObject<UserType> user =
          getModelService().getObject(UserType.class, userOid, null, task, subResult);

      dto.setSecurityAnswers(createUsersSecurityQuestionsList(user));

      subResult.recordSuccessIfUnknown();

    } catch (Exception ex) {
      LoggingUtils.logExceptionOnDebugLevel(
          LOGGER, "Couldn't get user Questions, Probably not set yet", ex);

    } finally {
      result.recomputeStatus();
    }
    return dto;
  }

  public List<SecurityQuestionAnswerDTO> createUsersSecurityQuestionsList(
      PrismObject<UserType> user) {
    LOGGER.debug("Security Questions Loading for user: "******"User SecurityQuestion ANswer List is Not null");
        List<SecurityQuestionAnswerDTO> secQuestAnswListDTO =
            new ArrayList<SecurityQuestionAnswerDTO>();
        for (Iterator iterator = secQuestAnsList.iterator(); iterator.hasNext(); ) {
          SecurityQuestionAnswerType securityQuestionAnswerType =
              (SecurityQuestionAnswerType) iterator.next();

          Protector protector = getPrismContext().getDefaultProtector();
          String decoded = "";
          if (securityQuestionAnswerType.getQuestionAnswer().getEncryptedDataType() != null) {
            try {
              decoded = protector.decryptString(securityQuestionAnswerType.getQuestionAnswer());

            } catch (EncryptionException e) {
              LoggingUtils.logException(LOGGER, "Couldn't decrypt user answer", e);
            }
          }
          // LOGGER.debug("SecAnswerIdentifier:"+securityQuestionAnswerType.getQuestionIdentifier());
          secQuestAnswListDTO.add(
              new SecurityQuestionAnswerDTO(
                  securityQuestionAnswerType.getQuestionIdentifier(), decoded));
        }

        return secQuestAnswListDTO;
      }
    }
    return null;
  }

  public void initLayout() {

    Form mainForm = new Form(ID_MAIN_FORM);

    // question panel list
    pqPanels = new ArrayList<MyPasswordQuestionsPanel>();
    OperationResult result = new OperationResult(OPERATION_LOAD_QUESTION_POLICY);
    try {

      Task task = getPageBase().createSimpleTask(OPERATION_LOAD_QUESTION_POLICY);
      OperationResult subResult = result.createSubresult(OPERATION_LOAD_QUESTION_POLICY);
      try {
        // PrismObject<SystemConfigurationType> config = getPageBase().getModelService().getObject(
        //	SystemConfigurationType.class, SystemObjectsType.SYSTEM_CONFIGURATION.value(), null,
        // task, result);

        CredentialsPolicyType credPolicy =
            getModelInteractionService().getCredentialsPolicy(null, null, result);

        //	PrismObject<SecurityPolicyType> securityPolicy =
        // getModelService().getObject(SecurityPolicyType.class,config.asObjectable().getGlobalSecurityPolicyRef().getOid(), null, task, subResult);
        // Global Policy set question numbers
        if (credPolicy != null && credPolicy.getSecurityQuestions() != null) {
          questionNumber = credPolicy.getSecurityQuestions().getQuestionNumber();

          // Actual Policy Question List
          policyQuestionList = credPolicy.getSecurityQuestions().getQuestion();
        } else {
          questionNumber = 0;
          policyQuestionList = new ArrayList<SecurityQuestionDefinitionType>();
        }
      } catch (Exception ex) {
        ex.printStackTrace();

        /*	List<SecurityQuestionAnswerDTO> userQuestionList= model.getObject().getSecurityAnswers();
        	int panelNumber=0;
        	PrismObject<UserType> user = null;



        	Collection options = SelectorOptions.createCollection(UserType.F_CREDENTIALS,
        			GetOperationOptions.createRetrieve(RetrieveOption.INCLUDE));
        	Task taskTwo = createSimpleTask("LOAD USER WRAPPER");
        	user = getModelService().getObject(UserType.class, SecurityUtils.getPrincipalUser().getOid(), options, taskTwo, result);

        	OperationResult parentResult = new OperationResult(OPERATION_LOAD_QUESTION_POLICY);
        	questionNumber = getModelInteractionService().getCredentialsPolicy(user, parentResult).getSecurityQuestions().getQuestionNumber();

        	policyQuestionList=getModelInteractionService().getCredentialsPolicy(user, parentResult).getSecurityQuestions().getQuestion();
        	if(userQuestionList==null){

        		executeAddingQuestions(questionNumber, 0, policyQuestionList);

        		LOGGER.info(getModelInteractionService().getCredentialsPolicy(user, parentResult).getSecurityQuestions().getQuestionNumber().toString());

        	}else{
        		for(int userQuestint=0;userQuestint<userQuestionList.size();userQuestint++){
        			SecurityQuestionAnswerDTO answerDTO=  checkIfQuestionisValid(userQuestionList.get(userQuestint), policyQuestionList);
        			if (userQuestionList.get(userQuestint)!=null){
        				LOGGER.debug("Questitself"+userQuestionList.get(userQuestint).getQuestionItself());
        				MyPasswordQuestionsPanel panel=new MyPasswordQuestionsPanel(ID_PASSWORD_QUESTIONS_PANEL+ panelNumber,userQuestionList.get(userQuestint));
        				pqPanels.add(panel);
        				panelNumber++;
        			}

        		}
        		//TODO same questions check should be implemented

        	}
        	add(mainForm);
        	mainForm.add(getPanels(pqPanels));

        	initButtons(mainForm);
        	return;
        */
      }

      /*User's Pre-Set Question List*/
      List<SecurityQuestionAnswerDTO> userQuestionList = model.getObject().getSecurityAnswers();

      /* check if user's set number of
       * questions matches the policy or not*/

      // Case that policy have more than users's number of numbers
      if ((userQuestionList == null) || (questionNumber > userQuestionList.size())) {
        if (userQuestionList == null) {
          executeAddingQuestions(questionNumber, 0, policyQuestionList);
          // TODO same questions check should be implemented

        } else {
          executePasswordQuestionsAndAnswers(
              userQuestionList, policyQuestionList, userQuestionList.size());
          // QUESTION NUMBER BIGGER THAN QUESTION LIST
          // rest of the questions
          int difference = questionNumber - userQuestionList.size();
          executeAddingQuestions(difference, userQuestionList.size(), policyQuestionList);
        }

      } else if (questionNumber == userQuestionList.size()) {
        // QUESTION NUMBER EQUALS TO QUESTION LIST
        executePasswordQuestionsAndAnswers(userQuestionList, policyQuestionList, 0);

        // TODO PART2: Case that policy have smaller than users's number of numbers
      } else if (questionNumber < userQuestionList.size()) {

        // QUESTION NUMBER SMALLER THAN QUESTION LIST
        executePasswordQuestionsAndAnswers(userQuestionList, policyQuestionList, 0);

        // this part will be using at remove operation in the future
        /*	int diff = userQuestionList.size()-questionNumber;
        for(Iterator iterator = userQuestionList.iterator(); iterator.hasNext();){

        	SecurityQuestionAnswerDTO element = (SecurityQuestionAnswerDTO)iterator.next();
        	for(int i=0; i<diff;i++){
        		if(element == userQuestionList.get(questionNumber+i)){

        			try{
        				//LOGGER.info("REMOVE");
        				iterator.remove();
        			} catch (UnsupportedOperationException uoe) {
        	            LOGGER.info(uoe.getStackTrace().toString());
        	        }
        	     }
        	}
        }*/
      }

    } catch (Exception ex) {

      result.recordFatalError("Couldn't load system configuration.", ex);
    }

    add(mainForm);
    mainForm.add(getPanels(pqPanels));
    initButtons(mainForm);
  }

  /**
   * method for adding questions to user credentials
   *
   * @author oguzhan
   * @param questionNumber
   * @param panelNumber
   * @param policyQuestionList
   */
  public void executeAddingQuestions(
      int questionNumber,
      int panelNumber,
      List<SecurityQuestionDefinitionType> policyQuestionList) {
    LOGGER.debug("executeAddingQuestions");
    for (int i = 0; i < questionNumber; i++) {
      // LOGGER.info("\n\n Adding panel element");
      SecurityQuestionAnswerDTO a =
          new SecurityQuestionAnswerDTO(
              policyQuestionList.get(panelNumber).getIdentifier(),
              "",
              policyQuestionList.get(panelNumber).getQuestionText());
      MyPasswordQuestionsPanel panel =
          new MyPasswordQuestionsPanel(ID_PASSWORD_QUESTIONS_PANEL + panelNumber, a);
      pqPanels.add(panel);
      panelNumber++;
    }
  }

  /**
   * method for get existing questions and answer from user credentials
   *
   * @author oguzhan
   * @param userQuestionList
   * @param policyQuestionList
   * @param panelNumber
   */
  public void executePasswordQuestionsAndAnswers(
      List<SecurityQuestionAnswerDTO> userQuestionList,
      List<SecurityQuestionDefinitionType> policyQuestionList,
      int panelNumber) {
    int userQuest = 0;
    LOGGER.debug("executePasswordQuestionsAndAnswers");
    for (Iterator iterator = policyQuestionList.iterator(); iterator.hasNext(); ) {

      /* Loop for finding the Existing Questions
       * and Answers according to Policy*/

      SecurityQuestionDefinitionType securityQuestionDefinitionType =
          (SecurityQuestionDefinitionType) iterator.next();
      // user's question List loop to match the questions
      for (int i = userQuest; i < userQuestionList.size(); i++) {

        if (userQuestionList
                .get(i)
                .getPwdQuestion()
                .trim()
                .compareTo(securityQuestionDefinitionType.getIdentifier().trim())
            == 0) {

          SecurityQuestionAnswerDTO a =
              new SecurityQuestionAnswerDTO(
                  userQuestionList.get(i).getPwdQuestion(),
                  userQuestionList.get(i).getPwdAnswer(),
                  userQuestionList.get(i).getQuestionItself());

          a = checkIfQuestionisValidSingle(a, securityQuestionDefinitionType);
          MyPasswordQuestionsPanel panel =
              new MyPasswordQuestionsPanel(ID_PASSWORD_QUESTIONS_PANEL + panelNumber, a);
          pqPanels.add(panel);
          panelNumber++;
          userQuest++;
          break;

        } else if (userQuestionList
                .get(i)
                .getPwdQuestion()
                .trim()
                .compareTo(securityQuestionDefinitionType.getIdentifier().trim())
            != 0) {

          SecurityQuestionAnswerDTO a =
              new SecurityQuestionAnswerDTO(
                  policyQuestionList.get(panelNumber).getIdentifier(),
                  "",
                  policyQuestionList.get(panelNumber).getQuestionText());
          a.setQuestionItself(securityQuestionDefinitionType.getQuestionText());
          userQuestionList
              .get(i)
              .setPwdQuestion(securityQuestionDefinitionType.getIdentifier().trim());

          MyPasswordQuestionsPanel panel =
              new MyPasswordQuestionsPanel(ID_PASSWORD_QUESTIONS_PANEL + panelNumber, a);
          pqPanels.add(panel);
          panelNumber++;

          userQuest++;
          break;
        }
      }
    }
  }

  public ListView<MyPasswordQuestionsPanel> getPanels(List<MyPasswordQuestionsPanel> p) {
    ListView lw =
        new ListView(ID_PASSWORD_QUESTIONS_PANEL, p) {
          @Override
          protected void populateItem(ListItem item) {

            item.add((MyPasswordQuestionsPanel) item.getModelObject());
          }
        };
    return lw;
  }

  public void initButtons(Form mainForm) {
    AjaxSubmitButton save =
        new AjaxSubmitButton(ID_SAVE, createStringResource("PageBase.button.save")) {

          private static final long serialVersionUID = 1L;

          @Override
          protected void onSubmit(AjaxRequestTarget target, Form<?> form) {

            savePerformed(target);
          }
        };
    mainForm.add(save);

    AjaxButton back =
        new AjaxButton(ID_BACK, createStringResource("PageBase.button.back")) {

          private static final long serialVersionUID = 1L;

          @Override
          public void onClick(AjaxRequestTarget target) {
            cancelPerformed(target);
          }
        };
    mainForm.add(back);
  }

  private void savePerformed(AjaxRequestTarget target) {

    /*
     * Oguzhan: added target variable to the updateQuestions method.
     */
    updateQuestions(SecurityUtils.getPrincipalUser().getOid(), target);
  }

  private void cancelPerformed(AjaxRequestTarget target) {
    if (WebMiscUtil.isAuthorized(
        AuthorizationConstants.AUTZ_UI_DASHBOARD_URL,
        AuthorizationConstants.AUTZ_UI_HOME_ALL_URL)) {
      setResponsePage(PageDashboard.class);
    } else {
      setResponsePage(PageSelfDashboard.class);
    }
  }

  private ObjectWrapper loadUserWrapper(PrismObject<UserType> userToEdit) {
    OperationResult result = new OperationResult(OPERATION_LOAD_USER);
    PrismObject<UserType> user = null;
    try {

      Collection options =
          SelectorOptions.createCollection(
              UserType.F_CREDENTIALS, GetOperationOptions.createRetrieve(RetrieveOption.INCLUDE));
      Task task = createSimpleTask(OPERATION_LOAD_USER);
      user =
          getModelService()
              .getObject(
                  UserType.class, SecurityUtils.getPrincipalUser().getOid(), options, task, result);

      result.recordSuccess();
    } catch (Exception ex) {
      result.recordFatalError("Couldn't get user.", ex);
      LoggingUtils.logException(LOGGER, "Couldn't load user PageMyQuestions", ex);
    }

    if (!result.isSuccess()) {
      showResultInSession(result);
    }

    if (user == null) {

      throw new RestartResponseException(PageDashboard.class);
    }

    ContainerStatus status = ContainerStatus.MODIFYING;
    ObjectWrapperFactory owf = new ObjectWrapperFactory(this);
    ObjectWrapper wrapper;
    try {
      wrapper = owf.createObjectWrapper("pageMyPasswordQuestions.userDetails", null, user, status);
    } catch (Exception ex) {
      result.recordFatalError("Couldn't get user.", ex);
      LoggingUtils.logException(LOGGER, "Couldn't load user", ex);
      wrapper =
          owf.createObjectWrapper(
              "pageMyPasswordQuestions.userDetails", null, user, null, null, status, false);
    }
    //        ObjectWrapper wrapper = new ObjectWrapper("pageUser.userDetails", null, user, status);
    if (owf.getResult() != null && !WebMiscUtil.isSuccessOrHandledError(wrapper.getResult())) {
      showResultInSession(wrapper.getResult());
    }

    return wrapper;
  }

  private SecurityQuestionAnswerDTO checkIfQuestionisValid(
      SecurityQuestionAnswerDTO questionIdentifier,
      List<SecurityQuestionDefinitionType> securityQuestionList) {

    for (Iterator iterator = securityQuestionList.iterator(); iterator.hasNext(); ) {
      SecurityQuestionDefinitionType securityQuestionDefinitionType =
          (SecurityQuestionDefinitionType) iterator.next();
      LOGGER.debug("List For" + securityQuestionDefinitionType.getIdentifier().trim());
      if (securityQuestionDefinitionType
          .getIdentifier()
          .trim()
          .equalsIgnoreCase((questionIdentifier.getPwdQuestion().trim()))) {
        questionIdentifier.setQuestionItself(securityQuestionDefinitionType.getQuestionText());

        LOGGER.info(": TRUE QUESTION");
        return questionIdentifier;
      } else {
        return null;
      }
    }

    return null;
  }

  private SecurityQuestionAnswerDTO checkIfQuestionisValidSingle(
      SecurityQuestionAnswerDTO questionIdentifier,
      SecurityQuestionDefinitionType securityQuestion) {

    if (securityQuestion
            .getIdentifier()
            .trim()
            .compareTo(questionIdentifier.getPwdQuestion().trim())
        == 0) {
      questionIdentifier.setQuestionItself(securityQuestion.getQuestionText());

      // LOGGER.info("\n\n: TRUE QUESTION");
      return questionIdentifier;
    } else {
      return null;
    }
  }

  private void updateQuestions(String useroid, AjaxRequestTarget target) {

    Task task = createSimpleTask(OPERATION_SAVE_QUESTIONS);
    OperationResult result = new OperationResult(OPERATION_SAVE_QUESTIONS);
    SchemaRegistry registry = getPrismContext().getSchemaRegistry();
    SecurityQuestionAnswerType[] answerTypeList = new SecurityQuestionAnswerType[questionNumber];

    try {
      int listnum = 0;
      for (Iterator iterator = pqPanels.iterator(); iterator.hasNext(); ) {
        MyPasswordQuestionsPanel type = (MyPasswordQuestionsPanel) iterator.next();

        SecurityQuestionAnswerType answerType = new SecurityQuestionAnswerType();
        ProtectedStringType answer = new ProtectedStringType();

        answer.setClearValue(
            ((TextField<String>) type.get(MyPasswordQuestionsPanel.F_ANSWER)).getModelObject());
        answerType.setQuestionAnswer(answer);

        // used apache's unescapeHtml method for special chars like \'
        String results =
            StringEscapeUtils.unescapeHtml(
                (type.get(MyPasswordQuestionsPanel.F_QUESTION)).getDefaultModelObjectAsString());
        answerType.setQuestionIdentifier(getQuestionIdentifierFromQuestion(results));
        answerTypeList[listnum] = answerType;
        listnum++;
      }

      // if(answerTypeList.length !=)

      // fill in answerType data here
      ItemPath path =
          new ItemPath(
              UserType.F_CREDENTIALS,
              CredentialsType.F_SECURITY_QUESTIONS,
              SecurityQuestionsCredentialsType.F_QUESTION_ANSWER);
      ObjectDelta<UserType> objectDelta =
          ObjectDelta.createModificationReplaceContainer(
              UserType.class, useroid, path, getPrismContext(), answerTypeList);

      Collection<ObjectDelta<? extends ObjectType>> deltas =
          MiscSchemaUtil.createCollection(objectDelta);
      getModelService().executeChanges(deltas, null, task, result);

      /*
      	System.out.println("getModel");
      	 Collection<ObjectDelta<? extends ObjectType>> deltas = new ArrayList<ObjectDelta<? extends ObjectType>>();
      	PasswordQuestionsDto dto = new PasswordQuestionsDto();
      	 PrismObjectDefinition objDef =registry.findObjectDefinitionByCompileTimeClass(UserType.class);
      	 Class<? extends ObjectType> type =  UserType.class;

      	 final ItemPath valuePath = new ItemPath(SchemaConstantsGenerated.C_CREDENTIALS,
                       CredentialsType.F_SECURITY_QUESTIONS, SecurityQuestionsCredentialsType.F_QUESTION_ANSWER);
      	 SecurityQuestionAnswerType secQuesAnsType= new SecurityQuestionAnswerType();
      	 ProtectedStringType protStrType= new ProtectedStringType();
      	 protStrType.setClearValue("deneme");
      	 secQuesAnsType.setQuestionAnswer(protStrType);
      	 dto.setSecurityAnswers(new ArrayList<SecurityQuestionAnswerType>());
      	 dto.getSecurityAnswers().add(secQuesAnsType);

      	PropertyDelta delta = PropertyDelta.createModificationReplaceProperty(valuePath, objDef, dto.getSecurityAnswers().get(0).getQuestionAnswer());
      //	PropertyDelta delta= PropertyDelta.createModifica

      	 System.out.println("Update Questions3");
      	deltas.add(ObjectDelta.createModifyDelta(useroid, delta, type, getPrismContext()));
      	System.out.println("Update Questions4");
      	getModelService().executeChanges(deltas, null, createSimpleTask(OPERATION_SAVE_QUESTIONS), result);
      	System.out.println("Update Questions5");

      	 */
      success(getString("message.success"));
      target.add(getFeedbackPanel());
    } catch (Exception ex) {

      error(getString("message.error"));
      target.add(getFeedbackPanel());
      ex.printStackTrace();
    }
  }

  private String getQuestionIdentifierFromQuestion(String questionItself) {
    // LOGGER.info("\n\n QUESTION: "+questionItself);
    for (Iterator iterator = policyQuestionList.iterator(); iterator.hasNext(); ) {
      SecurityQuestionDefinitionType securityQuestionDefinitionType =
          (SecurityQuestionDefinitionType) iterator.next();
      if (questionItself.equalsIgnoreCase(securityQuestionDefinitionType.getQuestionText()))
        return securityQuestionDefinitionType.getIdentifier();
    }
    return null;
  }

  public PageBase getPageBase() {
    return (PageBase) getPage();
  }
}
/**
 * This is very simple task handler that causes the process to enter WAITING for OTHER_TASKS state.
 *
 * @author Pavol Mederly
 */
public class WaitForTasksTaskHandler implements TaskHandler {

  private static final transient Trace LOGGER =
      TraceManager.getTrace(WaitForTasksTaskHandler.class);
  public static final String HANDLER_URI =
      "http://midpoint.evolveum.com/xml/ns/public/task/wait-for-tasks/handler-3";

  private static WaitForTasksTaskHandler instance = null;
  private TaskManagerQuartzImpl taskManagerImpl;

  private WaitForTasksTaskHandler() {}

  public static void instantiateAndRegister(TaskManager taskManager) {
    if (instance == null) {
      instance = new WaitForTasksTaskHandler();
    }
    taskManager.registerHandler(HANDLER_URI, instance);
    instance.taskManagerImpl = (TaskManagerQuartzImpl) taskManager;
  }

  @Override
  public TaskRunResult run(Task task) {

    OperationResult result =
        task.getResult().createSubresult(WaitForTasksTaskHandler.class.getName() + ".run");
    result.recordInProgress();

    LOGGER.info("WaitForTasksTaskHandler run starting; in task " + task.getName());
    try {
      // todo resolve this brutal hack
      taskManagerImpl.pauseTask(task, TaskWaitingReason.OTHER, result);
      task.startWaitingForTasksImmediate(result);
    } catch (SchemaException e) {
      throw new SystemException(
          "Couldn't mark task as waiting for prerequisite tasks",
          e); // should not occur; will be handled by task runner
    } catch (ObjectNotFoundException e) {
      throw new SystemException(
          "Couldn't mark task as waiting for prerequisite tasks",
          e); // should not occur; will be handled by task runner
    }
    LOGGER.info("WaitForTasksTaskHandler run finishing; in task " + task.getName());

    result.computeStatus();

    TaskRunResult runResult = new TaskRunResult();
    runResult.setOperationResult(result);
    runResult.setProgress(task.getProgress()); // not to overwrite task's progress
    runResult.setRunResultStatus(TaskRunResultStatus.FINISHED);
    return runResult;
  }

  @Override
  public Long heartbeat(Task task) {
    return null; // not to overwrite progress information!
  }

  @Override
  public void refreshStatus(Task task) {}

  @Override
  public String getCategoryName(Task task) {
    return null; // hopefully we will never need to derive category from this handler! (category is
                 // filled-in when persisting tasks)
  }

  @Override
  public List<String> getCategoryNames() {
    return null;
  }
}
/** @author semancik */
public class AbstractInitializedModelIntegrationTest
    extends AbstractConfiguredModelIntegrationTest {

  private static final int NUM_FUNCTIONAL_ORGS = 6;
  private static final int NUM_PROJECT_ORGS = 3;

  protected static final Trace LOGGER =
      TraceManager.getTrace(AbstractInitializedModelIntegrationTest.class);

  @Autowired(required = true)
  protected MappingFactory mappingFactory;

  @Autowired(required = true)
  protected Clockwork clockwork;

  protected ProfilingLensDebugListener lensDebugListener;

  protected UserType userTypeJack;
  protected UserType userTypeBarbossa;
  protected UserType userTypeGuybrush;
  protected UserType userTypeElaine;

  protected DummyResource dummyResource;
  protected DummyResourceContoller dummyResourceCtl;
  protected ResourceType resourceDummyType;
  protected PrismObject<ResourceType> resourceDummy;

  protected DummyResource dummyResourceRed;
  protected DummyResourceContoller dummyResourceCtlRed;
  protected ResourceType resourceDummyRedType;
  protected PrismObject<ResourceType> resourceDummyRed;

  protected DummyResource dummyResourceBlue;
  protected DummyResourceContoller dummyResourceCtlBlue;
  protected ResourceType resourceDummyBlueType;
  protected PrismObject<ResourceType> resourceDummyBlue;

  protected DummyResource dummyResourceWhite;
  protected DummyResourceContoller dummyResourceCtlWhite;
  protected ResourceType resourceDummyWhiteType;
  protected PrismObject<ResourceType> resourceDummyWhite;

  protected DummyResource dummyResourceYellow;
  protected DummyResourceContoller dummyResourceCtlYellow;
  protected ResourceType resourceDummyYellowType;
  protected PrismObject<ResourceType> resourceDummyYellow;

  protected DummyResource dummyResourceGreen;
  protected DummyResourceContoller dummyResourceCtlGreen;
  protected ResourceType resourceDummyGreenType;
  protected PrismObject<ResourceType> resourceDummyGreen;

  protected DummyResource dummyResourceBlack;
  protected DummyResourceContoller dummyResourceCtlBlack;
  protected ResourceType resourceDummyBlackType;
  protected PrismObject<ResourceType> resourceDummyBlack;

  protected DummyResource dummyResourceOrange;
  protected DummyResourceContoller dummyResourceCtlOrange;
  protected ResourceType resourceDummyOrangeType;
  protected PrismObject<ResourceType> resourceDummyOrange;

  protected DummyResource dummyResourceUpcase;
  protected DummyResourceContoller dummyResourceCtlUpcase;
  protected ResourceType resourceDummyUpcaseType;
  protected PrismObject<ResourceType> resourceDummyUpcase;

  protected ResourceType resourceDummySchemalessType;
  protected PrismObject<ResourceType> resourceDummySchemaless;

  public AbstractInitializedModelIntegrationTest() {
    super();
  }

  @Override
  public void initSystem(Task initTask, OperationResult initResult) throws Exception {
    LOGGER.trace("initSystem");
    super.initSystem(initTask, initResult);

    mappingFactory.setProfiling(true);
    lensDebugListener = new ProfilingLensDebugListener();
    clockwork.setDebugListener(lensDebugListener);

    // Resources

    dummyResourceCtl = DummyResourceContoller.create(null);
    dummyResourceCtl.extendSchemaPirate();
    dummyResource = dummyResourceCtl.getDummyResource();
    dummyResourceCtl.addAttrDef(
        dummyResource.getAccountObjectClass(),
        DUMMY_ACCOUNT_ATTRIBUTE_SEA_NAME,
        String.class,
        false,
        false);
    resourceDummy =
        importAndGetObjectFromFile(
            ResourceType.class, getResourceDummyFile(), RESOURCE_DUMMY_OID, initTask, initResult);
    resourceDummyType = resourceDummy.asObjectable();
    dummyResourceCtl.setResource(resourceDummy);

    dummyResourceCtlRed = DummyResourceContoller.create(RESOURCE_DUMMY_RED_NAME, resourceDummyRed);
    dummyResourceCtlRed.extendSchemaPirate();
    dummyResourceRed = dummyResourceCtlRed.getDummyResource();
    resourceDummyRed =
        importAndGetObjectFromFile(
            ResourceType.class,
            RESOURCE_DUMMY_RED_FILE,
            RESOURCE_DUMMY_RED_OID,
            initTask,
            initResult);
    resourceDummyRedType = resourceDummyRed.asObjectable();
    dummyResourceCtlRed.setResource(resourceDummyRed);

    dummyResourceCtlBlue =
        DummyResourceContoller.create(RESOURCE_DUMMY_BLUE_NAME, resourceDummyBlue);
    dummyResourceCtlBlue.extendSchemaPirate();
    dummyResourceBlue = dummyResourceCtlBlue.getDummyResource();
    resourceDummyBlue =
        importAndGetObjectFromFile(
            ResourceType.class,
            getResourceDummyBlueFile(),
            RESOURCE_DUMMY_BLUE_OID,
            initTask,
            initResult);
    resourceDummyBlueType = resourceDummyBlue.asObjectable();
    dummyResourceCtlBlue.setResource(resourceDummyBlue);

    dummyResourceCtlWhite =
        DummyResourceContoller.create(RESOURCE_DUMMY_WHITE_NAME, resourceDummyWhite);
    dummyResourceCtlWhite.extendSchemaPirate();
    dummyResourceWhite = dummyResourceCtlWhite.getDummyResource();
    resourceDummyWhite =
        importAndGetObjectFromFile(
            ResourceType.class,
            RESOURCE_DUMMY_WHITE_FILENAME,
            RESOURCE_DUMMY_WHITE_OID,
            initTask,
            initResult);
    resourceDummyWhiteType = resourceDummyWhite.asObjectable();
    dummyResourceCtlWhite.setResource(resourceDummyWhite);

    dummyResourceCtlYellow =
        DummyResourceContoller.create(RESOURCE_DUMMY_YELLOW_NAME, resourceDummyYellow);
    dummyResourceCtlYellow.extendSchemaPirate();
    dummyResourceYellow = dummyResourceCtlYellow.getDummyResource();
    resourceDummyYellow =
        importAndGetObjectFromFile(
            ResourceType.class,
            RESOURCE_DUMMY_YELLOW_FILENAME,
            RESOURCE_DUMMY_YELLOW_OID,
            initTask,
            initResult);
    resourceDummyYellowType = resourceDummyYellow.asObjectable();
    dummyResourceCtlYellow.setResource(resourceDummyYellow);

    dummyResourceCtlGreen =
        DummyResourceContoller.create(RESOURCE_DUMMY_GREEN_NAME, resourceDummyGreen);
    dummyResourceCtlGreen.extendSchemaPirate();
    dummyResourceGreen = dummyResourceCtlGreen.getDummyResource();
    resourceDummyGreen =
        importAndGetObjectFromFile(
            ResourceType.class,
            getResourceDummyGreenFile(),
            RESOURCE_DUMMY_GREEN_OID,
            initTask,
            initResult);
    resourceDummyGreenType = resourceDummyGreen.asObjectable();
    dummyResourceCtlGreen.setResource(resourceDummyGreen);

    dummyResourceCtlBlack =
        DummyResourceContoller.create(RESOURCE_DUMMY_BLACK_NAME, resourceDummyBlack);
    dummyResourceCtlBlack.extendSchemaPirate();
    dummyResourceBlack = dummyResourceCtlBlack.getDummyResource();
    resourceDummyBlack =
        importAndGetObjectFromFile(
            ResourceType.class,
            RESOURCE_DUMMY_BLACK_FILENAME,
            RESOURCE_DUMMY_BLACK_OID,
            initTask,
            initResult);
    resourceDummyBlackType = resourceDummyBlack.asObjectable();
    dummyResourceCtlBlack.setResource(resourceDummyBlack);

    dummyResourceCtlOrange =
        DummyResourceContoller.create(RESOURCE_DUMMY_ORANGE_NAME, resourceDummyOrange);
    dummyResourceCtlOrange.extendSchemaPirate();
    dummyResourceOrange = dummyResourceCtlOrange.getDummyResource();
    resourceDummyOrange =
        importAndGetObjectFromFile(
            ResourceType.class,
            RESOURCE_DUMMY_ORANGE_FILENAME,
            RESOURCE_DUMMY_ORANGE_OID,
            initTask,
            initResult);
    resourceDummyOrangeType = resourceDummyOrange.asObjectable();
    dummyResourceCtlOrange.setResource(resourceDummyOrange);

    dummyResourceCtlUpcase =
        DummyResourceContoller.create(RESOURCE_DUMMY_UPCASE_NAME, resourceDummyUpcase);
    dummyResourceCtlUpcase.extendSchemaPirate();
    dummyResourceUpcase = dummyResourceCtlUpcase.getDummyResource();
    resourceDummyUpcase =
        importAndGetObjectFromFile(
            ResourceType.class,
            RESOURCE_DUMMY_UPCASE_FILE,
            RESOURCE_DUMMY_UPCASE_OID,
            initTask,
            initResult);
    resourceDummyUpcaseType = resourceDummyUpcase.asObjectable();
    dummyResourceCtlUpcase.setResource(resourceDummyUpcase);
    dummyResourceCtlUpcase.addGroup(GROUP_JOKER_DUMMY_UPCASE_NAME);

    resourceDummySchemaless =
        importAndGetObjectFromFile(
            ResourceType.class,
            RESOURCE_DUMMY_SCHEMALESS_FILENAME,
            RESOURCE_DUMMY_SCHEMALESS_OID,
            initTask,
            initResult);
    resourceDummySchemalessType = resourceDummySchemaless.asObjectable();

    postInitDummyResouce();

    dummyResourceCtl.addAccount(ACCOUNT_HERMAN_DUMMY_USERNAME, "Herman Toothrot", "Monkey Island");
    dummyResourceCtl.addAccount(
        ACCOUNT_GUYBRUSH_DUMMY_USERNAME, "Guybrush Threepwood", "Melee Island");
    dummyResourceCtl.addAccount(
        ACCOUNT_DAVIEJONES_DUMMY_USERNAME, "Davie Jones", "Davie Jones' Locker");
    dummyResourceCtl.addAccount(ACCOUNT_CALYPSO_DUMMY_USERNAME, "Tia Dalma", "Pantano River");

    dummyResourceCtl.addAccount(ACCOUNT_ELAINE_DUMMY_USERNAME, "Elaine Marley", "Melee Island");
    dummyResourceCtlRed.addAccount(ACCOUNT_ELAINE_DUMMY_USERNAME, "Elaine Marley", "Melee Island");
    dummyResourceCtlBlue.addAccount(ACCOUNT_ELAINE_DUMMY_USERNAME, "Elaine Marley", "Melee Island");

    repoAddObjectFromFile(LOOKUP_LANGUAGES_FILE, ObjectTemplateType.class, initResult);

    repoAddObjectFromFile(SECURITY_POLICY_FILE, SecurityPolicyType.class, initResult);

    // User Templates
    repoAddObjectFromFile(USER_TEMPLATE_FILENAME, ObjectTemplateType.class, initResult);
    repoAddObjectFromFile(USER_TEMPLATE_COMPLEX_FILENAME, ObjectTemplateType.class, initResult);
    repoAddObjectFromFile(
        USER_TEMPLATE_COMPLEX_INCLUDE_FILENAME, ObjectTemplateType.class, initResult);
    repoAddObjectFromFile(
        USER_TEMPLATE_ORG_ASSIGNMENT_FILENAME, ObjectTemplateType.class, initResult);

    // Shadows
    repoAddObjectFromFile(ACCOUNT_SHADOW_GUYBRUSH_DUMMY_FILE, ShadowType.class, initResult);
    repoAddObjectFromFile(ACCOUNT_SHADOW_ELAINE_DUMMY_FILE, ShadowType.class, initResult);
    repoAddObjectFromFile(ACCOUNT_SHADOW_ELAINE_DUMMY_RED_FILE, ShadowType.class, initResult);
    repoAddObjectFromFile(ACCOUNT_SHADOW_ELAINE_DUMMY_BLUE_FILE, ShadowType.class, initResult);
    repoAddObjectFromFile(GROUP_SHADOW_JOKER_DUMMY_UPCASE_FILE, ShadowType.class, initResult);

    // Users
    userTypeJack =
        repoAddObjectFromFile(USER_JACK_FILE, UserType.class, true, initResult).asObjectable();
    userTypeBarbossa =
        repoAddObjectFromFile(USER_BARBOSSA_FILE, UserType.class, initResult).asObjectable();
    userTypeGuybrush =
        repoAddObjectFromFile(USER_GUYBRUSH_FILE, UserType.class, initResult).asObjectable();
    userTypeElaine =
        repoAddObjectFromFile(USER_ELAINE_FILENAME, UserType.class, initResult).asObjectable();

    // Roles
    repoAddObjectFromFile(ROLE_PIRATE_FILE, RoleType.class, initResult);
    repoAddObjectFromFile(ROLE_NICE_PIRATE_FILENAME, RoleType.class, initResult);
    repoAddObjectFromFile(ROLE_CAPTAIN_FILENAME, RoleType.class, initResult);
    repoAddObjectFromFile(ROLE_JUDGE_FILE, RoleType.class, initResult);
    repoAddObjectFromFile(ROLE_JUDGE_DEPRECATED_FILE, RoleType.class, initResult);
    repoAddObjectFromFile(ROLE_THIEF_FILE, RoleType.class, initResult);
    repoAddObjectFromFile(ROLE_EMPTY_FILE, RoleType.class, initResult);
    repoAddObjectFromFile(ROLE_SAILOR_FILE, RoleType.class, initResult);

    // Orgstruct
    if (doAddOrgstruct()) {
      repoAddObjectsFromFile(ORG_MONKEY_ISLAND_FILE, OrgType.class, initResult);
    }
  }

  protected boolean doAddOrgstruct() {
    return true;
  }

  protected File getResourceDummyFile() {
    return RESOURCE_DUMMY_FILE;
  }

  protected File getResourceDummyBlueFile() {
    return RESOURCE_DUMMY_BLUE_FILE;
  }

  protected File getResourceDummyGreenFile() {
    return RESOURCE_DUMMY_GREEN_FILE;
  }

  protected void postInitDummyResouce() {
    // Do nothing be default. Concrete tests may override this.
  }

  protected void assertUserJack(PrismObject<UserType> user) {
    assertUserJack(user, USER_JACK_FULL_NAME, USER_JACK_GIVEN_NAME, USER_JACK_FAMILY_NAME);
  }

  protected void assertUserJack(PrismObject<UserType> user, String fullName) {
    assertUserJack(user, fullName, USER_JACK_GIVEN_NAME, USER_JACK_FAMILY_NAME);
  }

  protected void assertUserJack(
      PrismObject<UserType> user, String fullName, String givenName, String familyName) {
    assertUserJack(user, fullName, givenName, familyName, "Caribbean");
  }

  protected void assertUserJack(
      PrismObject<UserType> user,
      String name,
      String fullName,
      String givenName,
      String familyName,
      String locality) {
    assertUser(user, USER_JACK_OID, name, fullName, givenName, familyName, locality);
    UserType userType = user.asObjectable();
    PrismAsserts.assertEqualsPolyString(
        "Wrong jack honorificPrefix", "Cpt.", userType.getHonorificPrefix());
    PrismAsserts.assertEqualsPolyString(
        "Wrong jack honorificSuffix", "PhD.", userType.getHonorificSuffix());
    assertEquals(
        "Wrong jack emailAddress", "*****@*****.**", userType.getEmailAddress());
    assertEquals("Wrong jack telephoneNumber", "555-1234", userType.getTelephoneNumber());
    assertEquals("Wrong jack employeeNumber", "emp1234", userType.getEmployeeNumber());
    assertEquals("Wrong jack employeeType", "CAPTAIN", userType.getEmployeeType().get(0));
    if (locality == null) {
      assertNull("Locality sneaked to user jack", userType.getLocality());
    } else {
      PrismAsserts.assertEqualsPolyString("Wrong jack locality", locality, userType.getLocality());
    }
  }

  protected void assertUserJack(
      PrismObject<UserType> user,
      String fullName,
      String givenName,
      String familyName,
      String locality) {
    assertUserJack(user, USER_JACK_USERNAME, fullName, givenName, familyName, locality);
  }

  protected void assertDummyAccountShadowRepo(
      PrismObject<ShadowType> accountShadow, String oid, String username) throws SchemaException {
    assertAccountShadowRepo(accountShadow, oid, username, resourceDummyType);
  }

  protected void assertDummyGroupShadowRepo(
      PrismObject<ShadowType> accountShadow, String oid, String username) throws SchemaException {
    assertShadowRepo(
        accountShadow, oid, username, resourceDummyType, getGroupObjectClass(resourceDummyType));
  }

  protected void assertDummyAccountShadowModel(
      PrismObject<ShadowType> accountShadow, String oid, String username) throws SchemaException {
    assertShadowModel(
        accountShadow, oid, username, resourceDummyType, getAccountObjectClass(resourceDummyType));
  }

  protected void assertDummyGroupShadowModel(
      PrismObject<ShadowType> accountShadow, String oid, String username) throws SchemaException {
    assertShadowModel(
        accountShadow, oid, username, resourceDummyType, getGroupObjectClass(resourceDummyType));
  }

  protected void assertDummyAccountShadowModel(
      PrismObject<ShadowType> accountShadow, String oid, String username, String fullname)
      throws SchemaException {
    assertDummyAccountShadowModel(accountShadow, oid, username);
    IntegrationTestTools.assertAttribute(
        accountShadow, dummyResourceCtl.getAttributeFullnameQName(), fullname);
  }

  protected void setDefaultUserTemplate(String userTemplateOid)
      throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException {

    PrismObjectDefinition<SystemConfigurationType> objectDefinition =
        prismContext
            .getSchemaRegistry()
            .findObjectDefinitionByCompileTimeClass(SystemConfigurationType.class);

    Collection<? extends ItemDelta> modifications;

    if (userTemplateOid == null) {
      modifications =
          ReferenceDelta.createModificationReplaceCollection(
              SystemConfigurationType.F_DEFAULT_USER_TEMPLATE_REF, objectDefinition, null);
    } else {
      PrismReferenceValue userTemplateRefVal = new PrismReferenceValue(userTemplateOid);
      modifications =
          ReferenceDelta.createModificationReplaceCollection(
              SystemConfigurationType.F_DEFAULT_USER_TEMPLATE_REF,
              objectDefinition,
              userTemplateRefVal);
    }

    OperationResult result = new OperationResult("Aplying default user template");

    repositoryService.modifyObject(
        SystemConfigurationType.class,
        SystemObjectsType.SYSTEM_CONFIGURATION.value(),
        modifications,
        result);
    display("Aplying default user template result", result);
    result.computeStatus();
    TestUtil.assertSuccess("Aplying default user template failed (result)", result);
  }

  protected void assertMonkeyIslandOrgSanity()
      throws ObjectNotFoundException, SchemaException, SecurityViolationException,
          CommunicationException, ConfigurationException {
    assertMonkeyIslandOrgSanity(0);
  }

  protected void assertMonkeyIslandOrgSanity(int expectedFictional)
      throws ObjectNotFoundException, SchemaException, SecurityViolationException,
          CommunicationException, ConfigurationException {
    Task task =
        taskManager.createTaskInstance(
            AbstractInitializedModelIntegrationTest.class.getName()
                + ".assertMonkeyIslandOrgSanity");
    OperationResult result = task.getResult();

    PrismObject<OrgType> orgGovernorOffice =
        modelService.getObject(OrgType.class, ORG_GOVERNOR_OFFICE_OID, null, task, result);
    result.computeStatus();
    TestUtil.assertSuccess(result);
    OrgType orgGovernorOfficeType = orgGovernorOffice.asObjectable();
    assertEquals(
        "Wrong governor office name",
        PrismTestUtil.createPolyStringType("F0001"),
        orgGovernorOfficeType.getName());

    List<PrismObject<OrgType>> governorSubOrgs =
        searchOrg(ORG_GOVERNOR_OFFICE_OID, OrgFilter.Scope.ONE_LEVEL, task, result);
    if (verbose) display("governor suborgs", governorSubOrgs);
    assertEquals("Unexpected number of governor suborgs", 3, governorSubOrgs.size());

    List<PrismObject<OrgType>> functionalOrgs =
        searchOrg(ORG_GOVERNOR_OFFICE_OID, OrgFilter.Scope.SUBTREE, task, result);
    if (verbose) display("functional orgs (null)", functionalOrgs);
    assertEquals(
        "Unexpected number of functional orgs (null)",
        NUM_FUNCTIONAL_ORGS - 1 + expectedFictional,
        functionalOrgs.size());

    List<PrismObject<OrgType>> prootSubOrgs =
        searchOrg(ORG_PROJECT_ROOT_OID, OrgFilter.Scope.ONE_LEVEL, task, result);
    if (verbose) display("project root suborgs", prootSubOrgs);
    assertEquals("Unexpected number of governor suborgs", 2, prootSubOrgs.size());

    List<PrismObject<OrgType>> projectOrgs =
        searchOrg(ORG_PROJECT_ROOT_OID, OrgFilter.Scope.SUBTREE, task, result);
    if (verbose) display("project orgs (null)", projectOrgs);
    assertEquals(
        "Unexpected number of functional orgs (null)", NUM_PROJECT_ORGS - 1, projectOrgs.size());

    PrismObject<OrgType> orgScummBar =
        modelService.getObject(OrgType.class, ORG_SCUMM_BAR_OID, null, task, result);
    List<AssignmentType> scummBarInducements = orgScummBar.asObjectable().getInducement();
    assertEquals(
        "Unexpected number of scumm bar inducements: " + scummBarInducements,
        1,
        scummBarInducements.size());

    ResultHandler<OrgType> handler = getOrgSanityCheckHandler();
    if (handler != null) {
      modelService.searchObjectsIterative(OrgType.class, null, handler, null, task, result);
    }
  }

  protected ResultHandler<OrgType> getOrgSanityCheckHandler() {
    return null;
  }

  protected void assertShadowOperationalData(
      PrismObject<ShadowType> shadow,
      SynchronizationSituationType expectedSituation,
      Long timeBeforeSync) {
    ShadowType shadowType = shadow.asObjectable();
    SynchronizationSituationType actualSituation = shadowType.getSynchronizationSituation();
    assertEquals("Wrong situation in shadow " + shadow, expectedSituation, actualSituation);
    XMLGregorianCalendar actualTimestampCal = shadowType.getSynchronizationTimestamp();
    assert actualTimestampCal != null : "No synchronization timestamp in shadow " + shadow;
    if (timeBeforeSync != null) {
      long actualTimestamp = XmlTypeConverter.toMillis(actualTimestampCal);
      assert actualTimestamp >= timeBeforeSync
          : "Synchronization timestamp was not updated in shadow " + shadow;
    }
    // TODO: assert sync description
  }
}
Example #13
0
/**
 * @author katkav
 * @author lazyman
 */
@PageDescriptor(
    url = "/admin/services",
    action = {
      @AuthorizationAction(
          actionUri = PageAdminServices.AUTH_SERVICES_ALL,
          label = PageAdminServices.AUTH_SERVICES_ALL_LABEL,
          description = PageAdminServices.AUTH_SERVICES_ALL_DESCRIPTION),
      @AuthorizationAction(
          actionUri = AuthorizationConstants.AUTZ_UI_SERVICES_URL,
          label = "PageServices.auth.services.label",
          description = "PageServices.auth.services.description")
    })
public class PageServices extends PageAdminServices implements FocusListComponent {
  private static final long serialVersionUID = 1L;

  private static final String ID_MAIN_FORM = "mainForm";
  private static final String ID_TABLE = "table";
  private static final String DOT_CLASS = PageServices.class.getName() + ".";
  private static final Trace LOGGER = TraceManager.getTrace(PageServices.class);
  private static final String OPERATION_DELETE_SERVICES = DOT_CLASS + "deleteServices";

  private IModel<Search> searchModel;

  public PageServices() {
    this(true);
  }

  public PageServices(boolean clearPagingInSession) {
    initLayout();
  }

  private final FocusListInlineMenuHelper<ServiceType> listInlineMenuHelper =
      new FocusListInlineMenuHelper<>(ServiceType.class, this, this);

  private void initLayout() {
    Form mainForm = new Form(ID_MAIN_FORM);
    add(mainForm);

    MainObjectListPanel<ServiceType> servicePanel =
        new MainObjectListPanel<ServiceType>(
            ID_TABLE, ServiceType.class, TableId.TABLE_SERVICES, null, this) {
          private static final long serialVersionUID = 1L;

          @Override
          public void objectDetailsPerformed(AjaxRequestTarget target, ServiceType service) {
            PageServices.this.serviceDetailsPerformed(target, service);
          }

          @Override
          protected List<IColumn<SelectableBean<ServiceType>, String>> createColumns() {
            List<IColumn<SelectableBean<ServiceType>, String>> columns =
                ColumnUtils.getDefaultServiceColumns();

            IColumn column = new InlineMenuHeaderColumn(listInlineMenuHelper.initInlineMenu());
            columns.add(column);

            return columns;
          }

          @Override
          protected List<InlineMenuItem> createInlineMenu() {
            return listInlineMenuHelper.createRowActions();
          }

          @Override
          protected void newObjectPerformed(AjaxRequestTarget target) {
            navigateToNext(PageService.class);
          }
        };
    servicePanel.setAdditionalBoxCssClasses(GuiStyleConstants.CLASS_OBJECT_SERVICE_BOX_CSS_CLASSES);
    servicePanel.setOutputMarkupId(true);
    mainForm.add(servicePanel);
  }

  protected void serviceDetailsPerformed(AjaxRequestTarget target, ServiceType service) {
    PageParameters parameters = new PageParameters();
    parameters.add(OnePageParameterEncoder.PARAMETER, service.getOid());
    navigateToNext(PageService.class, parameters);
  }

  @Override
  public MainObjectListPanel<ServiceType> getObjectListPanel() {
    return (MainObjectListPanel<ServiceType>) get(createComponentPath(ID_MAIN_FORM, ID_TABLE));
  }
}
/** @author lazyman */
@PageDescriptor(
    url = "/admin/resources/content/accounts",
    encoder = OnePageParameterEncoder.class,
    action = {
      @AuthorizationAction(
          actionUri = PageAdminResources.AUTH_RESOURCE_ALL,
          label = PageAdminResources.AUTH_RESOURCE_ALL_LABEL,
          description = PageAdminResources.AUTH_RESOURCE_ALL_DESCRIPTION),
      @AuthorizationAction(
          actionUri = AuthorizationConstants.AUTZ_UI_RESOURCES_CONTENT_ACCOUNTS_URL,
          label = "PageContentAccounts.auth.resourcesContentAccounts.label",
          description = "PageContentAccounts.auth.resourcesContentAccounts.description")
    })
public class PageContentAccounts extends PageAdminResources {

  private static final Trace LOGGER = TraceManager.getTrace(PageContentAccounts.class);

  private static final String DOT_CLASS = PageContentAccounts.class.getName() + ".";
  private static final String OPERATION_CHANGE_OWNER = DOT_CLASS + "changeOwner";
  private static final String OPERATION_CREATE_USER_FROM_ACCOUNTS =
      DOT_CLASS + "createUserFromAccounts";
  private static final String OPERATION_CREATE_USER_FROM_ACCOUNT =
      DOT_CLASS + "createUserFromAccount";
  private static final String OPERATION_DELETE_ACCOUNT_FROM_RESOURCE =
      DOT_CLASS + "deleteAccountFromResource";
  private static final String OPERATION_ADJUST_ACCOUNT_STATUS = "changeAccountActivationStatus";
  private static final String MODAL_ID_OWNER_CHANGE = "ownerChangePopup";
  private static final String MODAL_ID_CONFIRM_DELETE = "confirmDeletePopup";

  private static final String ID_MAIN_FORM = "mainForm";
  private static final String ID_BASIC_SEARCH = "basicSearch";
  private static final String ID_SEARCH_FORM = "searchForm";
  private static final String ID_NAME_CHECK = "nameCheck";
  private static final String ID_IDENTIFIERS_CHECK = "identifiersCheck";
  private static final String ID_TABLE = "table";

  private IModel<PrismObject<ResourceType>> resourceModel;
  private IModel<AccountContentSearchDto> searchModel;
  private LoadableModel<AccountOwnerChangeDto> ownerChangeModel;
  private AccountContentDto singleDelete;

  public PageContentAccounts() {
    searchModel =
        new LoadableModel<AccountContentSearchDto>() {

          @Override
          protected AccountContentSearchDto load() {
            ResourcesStorage storage = getSessionStorage().getResources();
            AccountContentSearchDto dto = storage.getAccountContentSearch();

            if (dto == null) {
              dto = new AccountContentSearchDto();
            }

            return dto;
          }
        };

    resourceModel =
        new LoadableModel<PrismObject<ResourceType>>(false) {

          @Override
          protected PrismObject<ResourceType> load() {
            if (!isResourceOidAvailable()) {
              getSession().error(getString("pageContentAccounts.message.resourceOidNotDefined"));
              throw new RestartResponseException(PageResources.class);
            }
            return loadResource(null);
          }
        };
    ownerChangeModel =
        new LoadableModel<AccountOwnerChangeDto>(false) {

          @Override
          protected AccountOwnerChangeDto load() {
            return new AccountOwnerChangeDto();
          }
        };

    initLayout();
  }

  private void initDialog() {
    UserBrowserDialog<UserType> dialog =
        new UserBrowserDialog<UserType>(MODAL_ID_OWNER_CHANGE, UserType.class) {

          @Override
          public void userDetailsPerformed(AjaxRequestTarget target, UserType user) {
            super.userDetailsPerformed(target, user);

            ownerChangePerformed(target, user);
            target.add(getTable());
          }
        };
    add(dialog);

    add(
        new ConfirmationDialog(
            MODAL_ID_CONFIRM_DELETE,
            createStringResource("pageContentAccounts.dialog.title.confirmDelete"),
            createDeleteConfirmString()) {

          @Override
          public void yesPerformed(AjaxRequestTarget target) {
            close(target);
            deleteConfirmedPerformed(target);
          }
        });
  }

  private IModel<String> createDeleteConfirmString() {
    return new AbstractReadOnlyModel<String>() {
      @Override
      public String getObject() {
        if (singleDelete == null) {
          return createStringResource(
                  "pageContentAccounts.message.deleteConfirmation",
                  getSelectedAccounts(null).size())
              .getString();
        } else {
          return createStringResource(
                  "pageContentAccounts.message.deleteConfirmationSingle",
                  singleDelete.getAccountName())
              .getString();
        }
      }
    };
  }

  private void initLayout() {
    Form searchForm = new Form(ID_SEARCH_FORM);
    add(searchForm);

    CheckBox nameCheck =
        new CheckBox(ID_NAME_CHECK, new PropertyModel(searchModel, AccountContentSearchDto.F_NAME));
    searchForm.add(nameCheck);

    CheckBox identifiersCheck =
        new CheckBox(
            ID_IDENTIFIERS_CHECK,
            new PropertyModel(searchModel, AccountContentSearchDto.F_IDENTIFIERS));
    searchForm.add(identifiersCheck);

    BasicSearchPanel<AccountContentSearchDto> basicSearch =
        new BasicSearchPanel<AccountContentSearchDto>(ID_BASIC_SEARCH) {

          @Override
          protected IModel<String> createSearchTextModel() {
            return new PropertyModel<>(searchModel, AccountContentSearchDto.F_SEARCH_TEXT);
          }

          @Override
          protected void searchPerformed(AjaxRequestTarget target) {
            PageContentAccounts.this.searchPerformed(target);
          }

          @Override
          protected void clearSearchPerformed(AjaxRequestTarget target) {
            PageContentAccounts.this.clearSearchPerformed(target);
          }
        };
    searchForm.add(basicSearch);

    Form mainForm = new Form(ID_MAIN_FORM);
    add(mainForm);

    AccountContentDataProvider provider =
        new AccountContentDataProvider(
            this,
            new PropertyModel<String>(resourceModel, "oid"),
            createObjectClassModel(),
            createUseObjectCountingModel()) {

          @Override
          protected void addInlineMenuToDto(AccountContentDto dto) {
            addRowMenuToTable(dto);
          }
        };
    provider.setQuery(createQuery());

    List<IColumn> columns = initColumns();
    TablePanel table =
        new TablePanel(
            ID_TABLE,
            provider,
            columns,
            UserProfileStorage.TableId.PAGE_RESOURCE_ACCOUNTS_PANEL,
            getItemsPerPage(UserProfileStorage.TableId.PAGE_RESOURCE_ACCOUNTS_PANEL));
    table.setOutputMarkupId(true);
    mainForm.add(table);

    initDialog();
  }

  private List<IColumn> initColumns() {
    List<IColumn> columns = new ArrayList<IColumn>();

    IColumn column = new CheckBoxColumn(new Model<String>(), AccountContentDto.F_SELECTED);
    columns.add(column);

    column =
        new LinkColumn<AccountContentDto>(
            createStringResource("pageContentAccounts.name"), AccountContentDto.F_ACCOUNT_NAME) {

          @Override
          public void onClick(AjaxRequestTarget target, IModel<AccountContentDto> rowModel) {
            AccountContentDto dto = rowModel.getObject();
            accountDetailsPerformed(target, dto.getAccountName(), dto.getAccountOid());
          }
        };
    columns.add(column);

    column =
        new AbstractColumn<AccountContentDto, String>(
            createStringResource("pageContentAccounts.identifiers")) {

          @Override
          public void populateItem(
              Item<ICellPopulator<AccountContentDto>> cellItem,
              String componentId,
              IModel<AccountContentDto> rowModel) {

            AccountContentDto dto = rowModel.getObject();
            List values = new ArrayList();
            for (ResourceAttribute<?> attr : dto.getIdentifiers()) {
              values.add(attr.getElementName().getLocalPart() + ": " + attr.getRealValue());
            }
            cellItem.add(new Label(componentId, new Model<>(StringUtils.join(values, ", "))));
          }
        };
    columns.add(column);

    column =
        new PropertyColumn(
            createStringResource("pageContentAccounts.kind"), AccountContentDto.F_KIND);
    columns.add(column);

    column =
        new PropertyColumn(
            createStringResource("pageContentAccounts.intent"), AccountContentDto.F_INTENT);
    columns.add(column);

    column =
        new PropertyColumn(
            createStringResource("pageContentAccounts.objectClass"),
            AccountContentDto.F_OBJECT_CLASS);
    columns.add(column);

    column =
        new EnumPropertyColumn(
            createStringResource("pageContentAccounts.situation"), AccountContentDto.F_SITUATION) {

          @Override
          protected String translate(Enum en) {
            return createStringResource(en).getString();
          }
        };
    columns.add(column);

    column =
        new LinkColumn<AccountContentDto>(createStringResource("pageContentAccounts.owner")) {

          @Override
          protected IModel<String> createLinkModel(final IModel<AccountContentDto> rowModel) {
            return new AbstractReadOnlyModel<String>() {

              @Override
              public String getObject() {
                AccountContentDto dto = rowModel.getObject();
                if (StringUtils.isNotBlank(dto.getOwnerName())) {
                  return dto.getOwnerName();
                }

                return dto.getOwnerOid();
              }
            };
          }

          @Override
          public void onClick(AjaxRequestTarget target, IModel<AccountContentDto> rowModel) {
            AccountContentDto dto = rowModel.getObject();

            ownerDetailsPerformed(target, dto.getOwnerName(), dto.getOwnerOid());
          }
        };
    columns.add(column);

    column = new InlineMenuHeaderColumn(createHeaderMenuItems());
    columns.add(column);

    return columns;
  }

  private List<InlineMenuItem> createHeaderMenuItems() {
    List<InlineMenuItem> items = new ArrayList<InlineMenuItem>();

    items.add(
        new InlineMenuItem(
            createStringResource("pageContentAccounts.menu.enableAccount"),
            true,
            new HeaderMenuAction(this) {

              @Override
              public void onSubmit(AjaxRequestTarget target, Form<?> form) {
                updateAccountStatusPerformed(target, null, true);
              }
            }));

    items.add(
        new InlineMenuItem(
            createStringResource("pageContentAccounts.menu.disableAccount"),
            true,
            new HeaderMenuAction(this) {

              @Override
              public void onSubmit(AjaxRequestTarget target, Form<?> form) {
                updateAccountStatusPerformed(target, null, false);
              }
            }));

    items.add(
        new InlineMenuItem(
            createStringResource("pageContentAccounts.menu.deleteAccount"),
            true,
            new HeaderMenuAction(this) {

              @Override
              public void onSubmit(AjaxRequestTarget target, Form<?> form) {
                deleteAccountPerformed(target, null);
              }
            }));

    items.add(new InlineMenuItem());

    items.add(
        new InlineMenuItem(
            createStringResource("pageContentAccounts.menu.importAccount"),
            true,
            new HeaderMenuAction(this) {

              @Override
              public void onSubmit(AjaxRequestTarget target, Form<?> form) {
                importAccount(target, null);
              }
            }));

    items.add(new InlineMenuItem());

    items.add(
        new InlineMenuItem(
            createStringResource("pageContentAccounts.menu.removeOwner"),
            true,
            new HeaderMenuAction(this) {

              @Override
              public void onSubmit(AjaxRequestTarget target, Form<?> form) {
                removeOwnerPerformed(target, null);
              }
            }));

    return items;
  }

  private void addRowMenuToTable(final AccountContentDto dto) {
    dto.getMenuItems()
        .add(
            new InlineMenuItem(
                createStringResource("pageContentAccounts.menu.enableAccount"),
                true,
                new HeaderMenuAction(this) {

                  @Override
                  public void onSubmit(AjaxRequestTarget target, Form<?> form) {
                    updateAccountStatusPerformed(target, dto, true);
                  }
                }));

    dto.getMenuItems()
        .add(
            new InlineMenuItem(
                createStringResource("pageContentAccounts.menu.disableAccount"),
                true,
                new HeaderMenuAction(this) {

                  @Override
                  public void onSubmit(AjaxRequestTarget target, Form<?> form) {
                    updateAccountStatusPerformed(target, dto, false);
                  }
                }));

    dto.getMenuItems()
        .add(
            new InlineMenuItem(
                createStringResource("pageContentAccounts.menu.deleteAccount"),
                true,
                new HeaderMenuAction(this) {

                  @Override
                  public void onSubmit(AjaxRequestTarget target, Form<?> form) {
                    deleteAccountPerformed(target, dto);
                  }
                }));

    dto.getMenuItems().add(new InlineMenuItem());

    dto.getMenuItems()
        .add(
            new InlineMenuItem(
                createStringResource("pageContentAccounts.menu.importAccount"),
                new ColumnMenuAction<UserListItemDto>() {

                  @Override
                  public void onClick(AjaxRequestTarget target) {
                    importAccount(target, dto);
                  }
                }));

    dto.getMenuItems().add(new InlineMenuItem());

    dto.getMenuItems()
        .add(
            new InlineMenuItem(
                createStringResource("pageContentAccounts.menu.changeOwner"),
                new ColumnMenuAction<UserListItemDto>() {

                  @Override
                  public void onClick(AjaxRequestTarget target) {
                    changeOwnerPerformed(target, dto);
                  }
                }));

    dto.getMenuItems()
        .add(
            new InlineMenuItem(
                createStringResource("pageContentAccounts.menu.removeOwner"),
                true,
                new HeaderMenuAction(this) {

                  @Override
                  public void onSubmit(AjaxRequestTarget target, Form<?> form) {
                    removeOwnerPerformed(target, dto);
                  }
                }));
  }

  @Override
  protected IModel<String> createPageSubTitleModel() {
    return new LoadableModel<String>(false) {

      @Override
      protected String load() {
        String name = WebMiscUtil.getName(resourceModel.getObject());
        return new StringResourceModel(
                "PageContentAccounts.subTitle", PageContentAccounts.this, null, null, name)
            .getString();
      }
    };
  }

  private void ownerDetailsPerformed(AjaxRequestTarget target, String ownerName, String ownerOid) {
    if (StringUtils.isEmpty(ownerOid)) {
      error(getString("pageContentAccounts.message.cantShowUserDetails", ownerName, ownerOid));
      target.add(getFeedbackPanel());
      return;
    }

    PageParameters parameters = new PageParameters();
    parameters.add(OnePageParameterEncoder.PARAMETER, ownerOid);
    setResponsePage(PageUser.class, parameters);
  }

  private void changeOwnerPerformed(AjaxRequestTarget target, AccountContentDto dto) {
    reloadOwnerChangeModel(dto.getAccountOid(), dto.getOwnerOid());

    showModalWindow(MODAL_ID_OWNER_CHANGE, target);
  }

  private void reloadOwnerChangeModel(String accountOid, String ownerOid) {
    ownerChangeModel.reset();

    AccountOwnerChangeDto changeDto = ownerChangeModel.getObject();

    changeDto.setAccountOid(accountOid);
    changeDto.setAccountType(ShadowType.COMPLEX_TYPE);

    changeDto.setOldOwnerOid(ownerOid);
  }

  private void importAccount(AjaxRequestTarget target, AccountContentDto row) {
    List<AccountContentDto> accounts = isAnythingSelected(target, row);
    if (accounts.isEmpty()) {
      return;
    }

    OperationResult result = new OperationResult(OPERATION_CREATE_USER_FROM_ACCOUNTS);
    for (AccountContentDto dto : accounts) {
      OperationResult subResult = result.createMinorSubresult(OPERATION_CREATE_USER_FROM_ACCOUNT);
      try {
        getModelService()
            .importFromResource(
                dto.getAccountOid(),
                createSimpleTask(OPERATION_CREATE_USER_FROM_ACCOUNT),
                subResult);
      } catch (Exception ex) {
        subResult.computeStatus(
            getString("pageContentAccounts.message.cantImportAccount", dto.getAccountOid()));
        LoggingUtils.logException(
            LOGGER,
            "Can't import account {},oid={}",
            ex,
            dto.getAccountName(),
            dto.getAccountOid());
      } finally {
        subResult.computeStatusIfUnknown();
      }
    }

    result.computeStatus();
    showResult(result);

    target.add(getFeedbackPanel());
    target.add(getTable());
  }

  private TablePanel getTable() {
    return (TablePanel) get(createComponentPath(ID_MAIN_FORM, ID_TABLE));
  }

  private void searchPerformed(AjaxRequestTarget target) {
    ObjectQuery query = createQuery();

    TablePanel panel = getTable();
    DataTable table = panel.getDataTable();
    AccountContentDataProvider provider = (AccountContentDataProvider) table.getDataProvider();
    provider.setQuery(query);
    table.setCurrentPage(0);

    target.add(panel);
    target.add(getFeedbackPanel());
  }

  private ObjectQuery createQuery() {
    AccountContentSearchDto dto = searchModel.getObject();
    if (StringUtils.isEmpty(dto.getSearchText())) {
      return null;
    }

    try {
      ObjectQuery query = null;

      List<ObjectFilter> conditions = new ArrayList<>();
      ObjectClassComplexTypeDefinition def = getAccountDefinition();
      if (dto.isIdentifiers()) {

        List<ResourceAttributeDefinition> identifiers = new ArrayList<>();
        if (def.getIdentifiers() != null) {
          identifiers.addAll(def.getIdentifiers());
        }

        // TODO set matching rule instead fo null
        for (ResourceAttributeDefinition attrDef : identifiers) {
          conditions.add(
              EqualFilter.createEqual(
                  new ItemPath(ShadowType.F_ATTRIBUTES, attrDef.getName()),
                  attrDef,
                  dto.getSearchText()));
        }
      }

      if (dto.isName()) {
        List<ResourceAttributeDefinition> secondaryIdentifiers = new ArrayList<>();
        if (def.getNamingAttribute() != null) {
          secondaryIdentifiers.add(def.getNamingAttribute());
        } else if (def.getSecondaryIdentifiers() != null) {
          secondaryIdentifiers.addAll(def.getSecondaryIdentifiers());
        }
        for (ResourceAttributeDefinition attrDef : secondaryIdentifiers) {
          conditions.add(
              SubstringFilter.createSubstring(
                  new ItemPath(ShadowType.F_ATTRIBUTES, attrDef.getName()),
                  attrDef,
                  dto.getSearchText()));
        }
      }

      if (!conditions.isEmpty()) {
        if (conditions.size() > 1) {
          query = ObjectQuery.createObjectQuery(OrFilter.createOr(conditions));
        } else {
          query = ObjectQuery.createObjectQuery(conditions.get(0));
        }
      }

      return query;
    } catch (Exception ex) {
      error(getString("pageUsers.message.queryError") + " " + ex.getMessage());
      LoggingUtils.logException(LOGGER, "Couldn't create query filter.", ex);
    }

    return null;
  }

  private IModel<QName> createObjectClassModel() {
    return new LoadableModel<QName>(false) {

      @Override
      protected QName load() {
        try {
          return getObjectClassDefinition();
        } catch (Exception ex) {
          throw new SystemException(ex.getMessage(), ex);
        }
      }
    };
  }

  private IModel<Boolean> createUseObjectCountingModel() {
    return new LoadableModel<Boolean>(false) {

      @Override
      protected Boolean load() {
        try {
          return isUseObjectCounting();
        } catch (Exception ex) {
          throw new SystemException(ex.getMessage(), ex);
        }
      }
    };
  }

  private QName getObjectClassDefinition() throws SchemaException {
    ObjectClassComplexTypeDefinition def = getAccountDefinition();
    return def != null ? def.getTypeName() : null;
  }

  private ObjectClassComplexTypeDefinition getAccountDefinition() throws SchemaException {
    MidPointApplication application = (MidPointApplication) getApplication();
    PrismObject<ResourceType> resource = resourceModel.getObject();
    RefinedResourceSchema refinedSchema =
        RefinedResourceSchema.getRefinedSchema(resource, application.getPrismContext());
    ObjectClassComplexTypeDefinition def =
        refinedSchema.findDefaultObjectClassDefinition(ShadowKindType.ACCOUNT);
    return def;
  }

  private boolean isUseObjectCounting() throws SchemaException {
    MidPointApplication application = (MidPointApplication) getApplication();
    PrismObject<ResourceType> resource = resourceModel.getObject();
    RefinedResourceSchema resourceSchema =
        RefinedResourceSchema.getRefinedSchema(resource, application.getPrismContext());

    // hacking this for now ... in future, we get the type definition (and maybe kind+intent)
    // directly from GUI model
    // TODO here we should deal with the situation that one object class is mentioned in different
    // kind/intent sections -- we would want to avoid mentioning paged search information in all
    // these sections
    ObjectClassComplexTypeDefinition typeDefinition = getAccountDefinition();
    if (typeDefinition == null) {
      // should not occur
      LOGGER.warn("ObjectClass definition couldn't be found");
      return false;
    }

    RefinedObjectClassDefinition refinedObjectClassDefinition =
        resourceSchema.getRefinedDefinition(typeDefinition.getTypeName());
    if (refinedObjectClassDefinition == null) {
      return false;
    }
    return refinedObjectClassDefinition.isObjectCountingEnabled();
  }

  private void showModalWindow(String id, AjaxRequestTarget target) {
    ModalWindow window = (ModalWindow) get(id);
    window.show(target);
  }

  private void accountDetailsPerformed(
      AjaxRequestTarget target, String accountName, String accountOid) {
    if (StringUtils.isEmpty(accountOid)) {
      error(
          getString("pageContentAccounts.message.cantShowAccountDetails", accountName, accountOid));
      target.add(getFeedbackPanel());
      return;
    }

    PageParameters parameters = new PageParameters();
    parameters.add(OnePageParameterEncoder.PARAMETER, accountOid);
    setResponsePage(PageAccount.class, parameters);
  }

  private List<AccountContentDto> getSelectedAccounts(AccountContentDto dto) {
    List<AccountContentDto> accounts;
    if (dto != null) {
      accounts = new ArrayList<>();
      accounts.add(dto);
    } else {
      accounts = WebMiscUtil.getSelectedData(getTable());
    }

    return accounts;
  }

  private List<AccountContentDto> isAnythingSelected(
      AjaxRequestTarget target, AccountContentDto dto) {
    List<AccountContentDto> accounts;
    if (dto != null) {
      accounts = new ArrayList<>();
      accounts.add(dto);
    } else {
      accounts = WebMiscUtil.getSelectedData(getTable());
      if (accounts.isEmpty()) {
        warn(getString("pageContentAccounts.message.noAccountSelected"));
        target.add(getFeedbackPanel());
      }
    }

    return accounts;
  }

  private void removeOwnerPerformed(AjaxRequestTarget target, AccountContentDto row) {
    List<AccountContentDto> accounts = isAnythingSelected(target, row);
    if (accounts.isEmpty()) {
      return;
    }

    for (AccountContentDto dto : accounts) {
      reloadOwnerChangeModel(dto.getAccountOid(), dto.getOwnerOid());
      ownerChangePerformed(target, null);
    }

    target.add(getTable());
    target.add(getFeedbackPanel());
  }

  private void ownerChangePerformed(AjaxRequestTarget target, UserType user) {
    AccountOwnerChangeDto dto = ownerChangeModel.getObject();
    OperationResult result = new OperationResult(OPERATION_CHANGE_OWNER);
    try {
      Task task = createSimpleTask(OPERATION_CHANGE_OWNER);
      if (StringUtils.isNotEmpty(dto.getOldOwnerOid())) {
        ObjectDelta delta = new ObjectDelta(UserType.class, ChangeType.MODIFY, getPrismContext());
        delta.setOid(dto.getOldOwnerOid());
        PrismReferenceValue refValue = new PrismReferenceValue(dto.getAccountOid());
        refValue.setTargetType(dto.getAccountType());
        delta.addModification(
            ReferenceDelta.createModificationDelete(
                UserType.class, UserType.F_LINK_REF, getPrismContext(), refValue));
        getModelService()
            .executeChanges(WebMiscUtil.createDeltaCollection(delta), null, task, result);
      }

      if (user != null) {
        ObjectDelta delta = new ObjectDelta(UserType.class, ChangeType.MODIFY, getPrismContext());
        delta.setOid(user.getOid());
        PrismReferenceValue refValue = new PrismReferenceValue(dto.getAccountOid());
        refValue.setTargetType(dto.getAccountType());
        delta.addModification(
            ReferenceDelta.createModificationAdd(
                UserType.class, UserType.F_LINK_REF, getPrismContext(), refValue));

        getModelService()
            .executeChanges(WebMiscUtil.createDeltaCollection(delta), null, task, result);
      }
      result.recomputeStatus();
    } catch (Exception ex) {
      result.recordFatalError("Couldn't submit user.", ex);
      LoggingUtils.logException(LOGGER, "Couldn't submit user", ex);
    }

    showResult(result);
    target.add(getFeedbackPanel());
  }

  private void clearSearchPerformed(AjaxRequestTarget target) {
    searchModel.setObject(new AccountContentSearchDto());

    TablePanel panel = getTable();
    DataTable table = panel.getDataTable();
    AccountContentDataProvider provider = (AccountContentDataProvider) table.getDataProvider();
    provider.setQuery(null);

    ResourcesStorage storage = getSessionStorage().getResources();
    storage.setAccountContentSearch(searchModel.getObject());
    storage.setAccountContentPaging(null);
    panel.setCurrentPage(null);

    target.add(get(ID_SEARCH_FORM));
    target.add(panel);
  }

  private void deleteAccountPerformed(AjaxRequestTarget target, AccountContentDto dto) {
    singleDelete = dto;
    List<AccountContentDto> accounts = isAnythingSelected(target, dto);

    if (accounts.isEmpty()) {
      return;
    }

    showModalWindow(MODAL_ID_CONFIRM_DELETE, target);
  }

  private void deleteConfirmedPerformed(AjaxRequestTarget target) {
    List<AccountContentDto> selected = new ArrayList<AccountContentDto>();

    if (singleDelete != null) {
      selected.add(singleDelete);
    } else {
      selected = isAnythingSelected(target, null);
    }

    OperationResult result = new OperationResult(OPERATION_DELETE_ACCOUNT_FROM_RESOURCE);

    for (AccountContentDto acc : selected) {
      String accOid = acc.getAccountOid();

      try {
        Task task = createSimpleTask(OPERATION_DELETE_ACCOUNT_FROM_RESOURCE);

        ObjectDelta delta =
            ObjectDelta.createDeleteDelta(ShadowType.class, accOid, getPrismContext());
        getModelService()
            .executeChanges(WebMiscUtil.createDeltaCollection(delta), null, task, result);

      } catch (Exception e) {
        result.recordPartialError("Couldn't delete account from resource.", e);
        LoggingUtils.logException(LOGGER, "Couldn't delete account from resource", e);
      }
    }

    if (result.isUnknown()) {
      result.recomputeStatus("Error occurred during resource account deletion.");
    }

    if (result.isSuccess()) {
      result.recordStatus(
          OperationResultStatus.SUCCESS, "Selected accounts have been successfully deleted.");
    }

    AccountContentDataProvider provider =
        (AccountContentDataProvider) getTable().getDataTable().getDataProvider();
    provider.clearCache();

    TablePanel table = getTable();
    target.add(table);
    showResult(result);
    target.add(getFeedbackPanel());
  }

  private void updateAccountStatusPerformed(
      AjaxRequestTarget target, AccountContentDto dto, boolean enabled) {
    List<AccountContentDto> accounts = isAnythingSelected(target, dto);
    OperationResult result = new OperationResult(OPERATION_ADJUST_ACCOUNT_STATUS);

    if (accounts.isEmpty()) {
      return;
    }

    ActivationStatusType status =
        enabled ? ActivationStatusType.ENABLED : ActivationStatusType.DISABLED;

    for (AccountContentDto acc : accounts) {

      ObjectDelta delta =
          ObjectDelta.createModificationReplaceProperty(
              ShadowType.class,
              acc.getAccountOid(),
              new ItemPath(ShadowType.F_ACTIVATION, ActivationType.F_ADMINISTRATIVE_STATUS),
              getPrismContext(),
              status);

      try {
        Task task = createSimpleTask(OPERATION_ADJUST_ACCOUNT_STATUS);
        getModelService()
            .executeChanges(WebMiscUtil.createDeltaCollection(delta), null, task, result);
      } catch (Exception e) {
        LoggingUtils.logException(LOGGER, "Couldn't enable/disable account(s) on resource", e);
        result.recordPartialError("Couldn't enable/disable account(s) on resource", e);
      }
    }
    result.recomputeStatus();
    showResult(result);
    target.add(getFeedbackPanel());
  }
}
Example #15
0
/** @author lazyman */
@PageDescriptor(
    url = "/admin/reports/created",
    action = {
      @AuthorizationAction(
          actionUri = PageAdminReports.AUTH_REPORTS_ALL,
          label = PageAdminConfiguration.AUTH_CONFIGURATION_ALL_LABEL,
          description = PageAdminConfiguration.AUTH_CONFIGURATION_ALL_DESCRIPTION),
      @AuthorizationAction(
          actionUri = AuthorizationConstants.AUTZ_UI_REPORTS_CREATED_REPORTS_URL,
          label = "PageCreatedReports.auth.createdReports.label",
          description = "PageCreatedReports.auth.createdReports.description")
    })
public class PageCreatedReports extends PageAdminReports {

  private static final Trace LOGGER = TraceManager.getTrace(PageCreatedReports.class);

  private static final String DOT_CLASS = PageCreatedReports.class.getName() + ".";
  private static final String OPERATION_DELETE = DOT_CLASS + "deleteReportOutput";
  private static final String OPERATION_DOWNLOAD_REPORT = DOT_CLASS + "downloadReport";

  private static final String ID_MAIN_FORM = "mainForm";
  private static final String ID_CREATED_REPORTS_TABLE = "table";
  private static final String ID_SEARCH_FORM = "searchForm";
  private static final String ID_BASIC_SEARCH = "basicSearch";
  private static final String ID_FILTER_FILE_TYPE = "filetype";
  private static final String ID_REPORT_TYPE_SELECT = "reportType";
  private static final String ID_CONFIRM_DELETE = "confirmDeletePopup";
  private static final String ID_TABLE_HEADER = "tableHeader";

  private LoadableModel<ReportOutputSearchDto> searchModel;
  private IModel<ReportDeleteDialogDto> deleteModel = new Model<>();
  private ReportOutputType currentReport;

  private static Map<ExportType, String> reportExportTypeMap = new HashMap<>();

  static {
    reportExportTypeMap.put(ExportType.CSV, "text/csv; charset=UTF-8");
    reportExportTypeMap.put(
        ExportType.DOCX,
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=UTF-8");
    reportExportTypeMap.put(ExportType.HTML, "text/html; charset=UTF-8");
    reportExportTypeMap.put(
        ExportType.ODS, "application/vnd.oasis.opendocument.spreadsheet; charset=UTF-8");
    reportExportTypeMap.put(
        ExportType.ODT, "application/vnd.oasis.opendocument.text; charset=UTF-8");
    reportExportTypeMap.put(ExportType.PDF, "application/pdf; charset=UTF-8");
    reportExportTypeMap.put(
        ExportType.PPTX,
        "application/vnd.openxmlformats-officedocument.presentationml.presentation; charset=UTF-8");
    reportExportTypeMap.put(ExportType.RTF, "application/rtf; charset=UTF-8");
    reportExportTypeMap.put(ExportType.XHTML, "application/xhtml+xml; charset=UTF-8");
    reportExportTypeMap.put(ExportType.XLS, "application/vnd.ms-excel; charset=UTF-8");
    reportExportTypeMap.put(
        ExportType.XLSX,
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=UTF-8");
    reportExportTypeMap.put(ExportType.XML, "application/xml; charset=UTF-8");
    reportExportTypeMap.put(ExportType.XML_EMBED, "text/xml; charset=UTF-8");
  }

  public PageCreatedReports() {
    this(new PageParameters(), null);
  }

  public PageCreatedReports(PageParameters pageParameters, PageBase previousPage) {
    super(pageParameters);

    setPreviousPage(previousPage);

    searchModel =
        new LoadableModel<ReportOutputSearchDto>(false) {

          @Override
          protected ReportOutputSearchDto load() {
            ReportsStorage storage = getSessionStorage().getReports();
            ReportOutputSearchDto dto = storage.getReportOutputSearch();

            if (dto != null) {
              return dto;
            }

            return createSearchDto();
          }
        };

    initLayout();
  }

  private ReportOutputSearchDto createSearchDto() {
    ReportOutputSearchDto dto = new ReportOutputSearchDto();
    Map<String, String> reportTypeMap = dto.getReportTypeMap();

    List<PrismObject<ReportType>> reportTypes =
        WebModelUtils.searchObjects(ReportType.class, null, null, getPageBase());
    LOGGER.debug("Found {} report types.", reportTypes.size());

    for (PrismObject o : reportTypes) {
      ReportType reportType = (ReportType) o.asObjectable();

      if (reportType.isParent()) {
        String name = WebMiscUtil.getName(o);
        reportTypeMap.put(name, reportType.getOid());
      }
    }

    StringValue param = getPage().getPageParameters().get(OnePageParameterEncoder.PARAMETER);
    if (param != null) {
      for (String key : dto.getReportTypeMap().keySet()) {
        if (reportTypeMap.get(key).equals(param.toString())) {
          dto.setReportType(key);
        }
      }
    }

    return dto;
  }

  private void initLayout() {
    Form mainForm = new Form(ID_MAIN_FORM);
    add(mainForm);

    final AjaxDownloadBehaviorFromStream ajaxDownloadBehavior =
        new AjaxDownloadBehaviorFromStream() {

          @Override
          protected InputStream initStream() {
            if (currentReport != null) {
              String contentType = reportExportTypeMap.get(currentReport.getExportType());
              if (StringUtils.isEmpty(contentType)) {
                contentType = "multipart/mixed; charset=UTF-8";
              }
              setContentType(contentType);
            }

            return createReport();
          }
        };

    mainForm.add(ajaxDownloadBehavior);

    ObjectDataProvider provider =
        new ObjectDataProvider(PageCreatedReports.this, ReportOutputType.class) {

          @Override
          protected void saveProviderPaging(ObjectQuery query, ObjectPaging paging) {
            ReportsStorage storage = getSessionStorage().getReports();
            storage.setReportOutputsPaging(paging);
          }

          @Override
          public ObjectQuery getQuery() {
            return createQuery();
          }
        };

    BoxedTablePanel table =
        new BoxedTablePanel(
            ID_CREATED_REPORTS_TABLE,
            provider,
            initColumns(ajaxDownloadBehavior),
            UserProfileStorage.TableId.PAGE_CREATED_REPORTS_PANEL,
            (int) getItemsPerPage(UserProfileStorage.TableId.PAGE_CREATED_REPORTS_PANEL)) {

          @Override
          protected WebMarkupContainer createHeader(String headerId) {
            return new SearchFragment(
                headerId, ID_TABLE_HEADER, PageCreatedReports.this, searchModel);
          }
        };
    table.setShowPaging(true);
    table.setOutputMarkupId(true);
    mainForm.add(table);

    add(
        new ConfirmationDialog(
            ID_CONFIRM_DELETE,
            createStringResource("pageCreatedReports.dialog.title.confirmDelete"),
            createDeleteConfirmString()) {

          @Override
          public void yesPerformed(AjaxRequestTarget target) {
            close(target);

            ReportDeleteDialogDto dto = deleteModel.getObject();
            switch (dto.getOperation()) {
              case DELETE_SINGLE:
                deleteSelectedConfirmedPerformed(target, Arrays.asList(dto.getObjects().get(0)));
                break;
              case DELETE_SELECTED:
                deleteSelectedConfirmedPerformed(target, dto.getObjects());
                break;
              case DELETE_ALL:
                deleteAllConfirmedPerformed(target);
                break;
            }
          }
        });
  }

  // TODO - commented until FileType property will be available in ReportOutputType

  public PageBase getPageBase() {
    return (PageBase) getPage();
  }

  // TODO - consider adding Author name, File Type and ReportType to columns
  private List<IColumn<SelectableBean<ReportOutputType>, String>> initColumns(
      final AjaxDownloadBehaviorFromStream ajaxDownloadBehavior) {
    List<IColumn<SelectableBean<ReportOutputType>, String>> columns = new ArrayList<>();

    IColumn column;

    column = new CheckBoxHeaderColumn();
    columns.add(column);

    column =
        new PropertyColumn(
            createStringResource("pageCreatedReports.table.name"), "name", "value.name");
    columns.add(column);

    column =
        new PropertyColumn(
            createStringResource("pageCreatedReports.table.description"), "value.description");
    columns.add(column);

    column =
        new AbstractColumn<SelectableBean<ReportOutputType>, String>(
            createStringResource("pageCreatedReports.table.time"), "createTimestamp") {

          @Override
          public void populateItem(
              Item<ICellPopulator<SelectableBean<ReportOutputType>>> cellItem,
              String componentId,
              final IModel<SelectableBean<ReportOutputType>> rowModel) {
            cellItem.add(
                new Label(
                    componentId,
                    new AbstractReadOnlyModel() {

                      @Override
                      public Object getObject() {
                        ReportOutputType object = rowModel.getObject().getValue();
                        MetadataType metadata = object.getMetadata();
                        if (metadata == null) {
                          return null;
                        }

                        return WebMiscUtil.formatDate(metadata.getCreateTimestamp());
                      }
                    }));
          }
        };
    columns.add(column);

    column =
        new AbstractColumn<SelectableBean<ReportOutputType>, String>(new Model(), null) {

          @Override
          public void populateItem(
              Item<ICellPopulator<SelectableBean<ReportOutputType>>> cellItem,
              String componentId,
              final IModel<SelectableBean<ReportOutputType>> model) {

            DownloadButtonPanel panel =
                new DownloadButtonPanel(componentId) {

                  @Override
                  protected void deletePerformed(AjaxRequestTarget target) {
                    deleteSelectedPerformed(
                        target,
                        ReportDeleteDialogDto.Operation.DELETE_SINGLE,
                        model.getObject().getValue());
                  }

                  @Override
                  protected void downloadPerformed(AjaxRequestTarget target) {
                    currentReport = model.getObject().getValue();
                    PageCreatedReports.this.downloadPerformed(
                        target, model.getObject().getValue(), ajaxDownloadBehavior);
                  }
                };
            cellItem.add(panel);
          }
        };
    columns.add(column);

    column =
        new InlineMenuHeaderColumn<InlineMenuable>(initInlineMenu()) {

          @Override
          public void populateItem(
              Item<ICellPopulator<InlineMenuable>> cellItem,
              String componentId,
              IModel<InlineMenuable> rowModel) {
            cellItem.add(new Label(componentId));
          }
        };
    columns.add(column);

    return columns;
  }

  private List<InlineMenuItem> initInlineMenu() {
    List<InlineMenuItem> headerMenuItems = new ArrayList<>();

    headerMenuItems.add(
        new InlineMenuItem(
            createStringResource("pageCreatedReports.inlineMenu.deleteAll"),
            true,
            new HeaderMenuAction(this) {

              @Override
              public void onSubmit(AjaxRequestTarget target, Form<?> form) {
                deleteAllPerformed(target, ReportDeleteDialogDto.Operation.DELETE_ALL);
              }
            }));

    headerMenuItems.add(
        new InlineMenuItem(
            createStringResource("pageCreatedReports.inlineMenu.deleteSelected"),
            true,
            new HeaderMenuAction(this) {

              @Override
              public void onSubmit(AjaxRequestTarget target, Form<?> form) {
                deleteSelectedPerformed(
                    target, ReportDeleteDialogDto.Operation.DELETE_SELECTED, null);
              }
            }));

    return headerMenuItems;
  }

  private IModel<String> createDeleteConfirmString() {
    return new AbstractReadOnlyModel<String>() {

      @Override
      public String getObject() {
        ReportDeleteDialogDto dto = deleteModel.getObject();

        switch (dto.getOperation()) {
          case DELETE_SINGLE:
            ReportOutputType report = dto.getObjects().get(0);
            return createStringResource(
                    "pageCreatedReports.message.deleteOutputSingle", report.getName().getOrig())
                .getString();
          case DELETE_ALL:
            return createStringResource("pageCreatedReports.message.deleteAll").getString();
          default:
            return createStringResource(
                    "pageCreatedReports.message.deleteOutputConfirmed", getSelectedData().size())
                .getString();
        }
      }
    };
  }

  private List<ReportOutputType> getSelectedData() {
    ObjectDataProvider<SelectableBean<ReportOutputType>, ReportOutputType> provider =
        getReportDataProvider();

    List<SelectableBean<ReportOutputType>> rows = provider.getAvailableData();
    List<ReportOutputType> selected = new ArrayList<>();

    for (SelectableBean<ReportOutputType> row : rows) {
      if (row.isSelected()) {
        selected.add(row.getValue());
      }
    }

    return selected;
  }

  private ObjectDataProvider<SelectableBean<ReportOutputType>, ReportOutputType>
      getReportDataProvider() {
    DataTable table = getReportOutputTable().getDataTable();
    return (ObjectDataProvider<SelectableBean<ReportOutputType>, ReportOutputType>)
        table.getDataProvider();
  }

  private Table getReportOutputTable() {
    return (Table) get(createComponentPath(ID_MAIN_FORM, ID_CREATED_REPORTS_TABLE));
  }

  private ObjectDataProvider getTableDataProvider() {
    Table tablePanel = getReportOutputTable();
    DataTable table = tablePanel.getDataTable();
    return (ObjectDataProvider) table.getDataProvider();
  }

  private void deleteAllPerformed(AjaxRequestTarget target, ReportDeleteDialogDto.Operation op) {
    ReportDeleteDialogDto dto = new ReportDeleteDialogDto(op, null);
    deleteModel.setObject(dto);

    ModalWindow dialog = (ModalWindow) get(ID_CONFIRM_DELETE);
    dialog.show(target);
  }

  private void deleteSelectedPerformed(
      AjaxRequestTarget target, ReportDeleteDialogDto.Operation op, ReportOutputType single) {
    List<ReportOutputType> selected = getSelectedData();

    if (single != null) {
      selected.clear();
      selected.add(single);
    }

    if (selected.isEmpty()) {
      return;
    }

    ReportDeleteDialogDto dto = new ReportDeleteDialogDto(op, selected);
    deleteModel.setObject(dto);

    ModalWindow dialog = (ModalWindow) get(ID_CONFIRM_DELETE);
    dialog.show(target);
  }

  private void deleteSelectedConfirmedPerformed(
      AjaxRequestTarget target, List<ReportOutputType> objects) {
    OperationResult result = new OperationResult(OPERATION_DELETE);

    for (ReportOutputType output : objects) {
      WebModelUtils.deleteObject(ReportOutputType.class, output.getOid(), result, this);
    }
    result.computeStatusIfUnknown();

    ObjectDataProvider provider = getTableDataProvider();
    provider.clearCache();

    showResult(result);
    target.add((Component) getReportOutputTable());
    target.add(getFeedbackPanel());
  }

  private void deleteAllConfirmedPerformed(AjaxRequestTarget target) {
    // TODO - implement as background task
    warn("Not implemented yet, will be implemented as background task.");
    target.add(getFeedbackPanel());
  }

  private ObjectQuery createQuery() {
    ReportOutputSearchDto dto = searchModel.getObject();

    try {
      List<ObjectFilter> ands = new ArrayList<>();

      if (StringUtils.isNotEmpty(dto.getText())) {
        PolyStringNormalizer normalizer = getPrismContext().getDefaultPolyStringNormalizer();
        String normalizedString = normalizer.normalize(dto.getText());

        SubstringFilter substring =
            SubstringFilter.createSubstring(
                ReportOutputType.F_NAME,
                ReportOutputType.class,
                getPrismContext(),
                PolyStringNormMatchingRule.NAME,
                normalizedString);
        ands.add(substring);
      }

      String oid = dto.getReportTypeMap().get(dto.getReportType());
      if (StringUtils.isNotEmpty(oid)) {
        RefFilter ref =
            RefFilter.createReferenceEqual(
                ReportOutputType.F_REPORT_REF, ReportOutputType.class, getPrismContext(), oid);
        ands.add(ref);
      }

      switch (ands.size()) {
        case 0:
          return null;
        case 1:
          return ObjectQuery.createObjectQuery(ands.get(0));
        default:
          AndFilter and = AndFilter.createAnd(ands);
          return ObjectQuery.createObjectQuery(and);
      }
    } catch (Exception e) {
      error(getString("pageCreatedReports.message.queryError") + " " + e.getMessage());
      LoggingUtils.logException(LOGGER, "Couldn't create query filter.", e);
      return null;
    }
  }

  private InputStream createReport() {
    OperationResult result = new OperationResult(OPERATION_DOWNLOAD_REPORT);
    ReportManager reportManager = getReportManager();

    if (currentReport == null) {
      return null;
    }

    InputStream input = null;
    try {
      input = reportManager.getReportOutputData(currentReport.getOid(), result);
    } catch (Exception e) {
      error(getString("pageCreatedReports.message.downloadError") + " " + e.getMessage());
      LoggingUtils.logException(LOGGER, "Couldn't download report.", e);
      LOGGER.trace(result.debugDump());
    } finally {
      result.computeStatusIfUnknown();
    }

    if (WebMiscUtil.showResultInPage(result)) {
      showResultInSession(result);
    }

    return input;
  }

  private void fileTypeFilterPerformed(AjaxRequestTarget target) {
    // TODO - perform filtering based on file type - need to wait for schema update
    // (ReportOutputType)
  }

  private void downloadPerformed(
      AjaxRequestTarget target,
      ReportOutputType report,
      AjaxDownloadBehaviorFromStream ajaxDownloadBehavior) {

    ajaxDownloadBehavior.initiate(target);
  }

  private void searchPerformed(AjaxRequestTarget target) {
    refreshTable(target);
  }

  private void clearSearchPerformed(AjaxRequestTarget target) {
    ReportOutputSearchDto dto = searchModel.getObject();
    dto.setReportType(null);
    dto.setText(null);

    refreshTable(target);
  }

  private void refreshTable(AjaxRequestTarget target) {
    Table panel = getReportOutputTable();

    ReportsStorage storage = getSessionStorage().getReports();
    storage.setReportOutputSearch(searchModel.getObject());
    storage.setReportOutputsPaging(null);
    panel.setCurrentPage(0);

    target.add((Component) panel);
    target.add(getFeedbackPanel());
  }

  private static class SearchFragment extends Fragment {

    public SearchFragment(
        String id,
        String markupId,
        MarkupContainer markupProvider,
        IModel<ReportOutputSearchDto> model) {
      super(id, markupId, markupProvider, model);

      initLayout();
    }

    private void initLayout() {
      final Form searchForm = new Form(ID_SEARCH_FORM);
      add(searchForm);
      searchForm.setOutputMarkupId(true);

      final IModel<ReportOutputSearchDto> model = (IModel) getDefaultModel();

      BasicSearchPanel<ReportOutputSearchDto> basicSearch =
          new BasicSearchPanel<ReportOutputSearchDto>(ID_BASIC_SEARCH, model) {

            @Override
            protected IModel<String> createSearchTextModel() {
              return new PropertyModel<String>(model, UsersDto.F_TEXT);
            }

            @Override
            protected void searchPerformed(AjaxRequestTarget target) {
              PageCreatedReports page = (PageCreatedReports) getPage();
              page.searchPerformed(target);
            }

            @Override
            protected void clearSearchPerformed(AjaxRequestTarget target) {
              PageCreatedReports page = (PageCreatedReports) getPage();
              page.clearSearchPerformed(target);
            }
          };
      searchForm.add(basicSearch);

      DropDownChoice reportTypeSelect =
          new DropDownChoice(
              ID_REPORT_TYPE_SELECT,
              new PropertyModel(model, ReportOutputSearchDto.F_REPORT_TYPE),
              new PropertyModel(model, ReportOutputSearchDto.F_REPORT_TYPES),
              new ChoiceRenderer()) {

            @Override
            protected String getNullValidDisplayValue() {
              return getString("pageCreatedReports.filter.reportType");
            }
          };
      reportTypeSelect.add(
          new OnChangeAjaxBehavior() {

            @Override
            protected void onUpdate(AjaxRequestTarget target) {
              PageCreatedReports page = (PageCreatedReports) getPage();
              page.searchPerformed(target);
            }
          });
      reportTypeSelect.setOutputMarkupId(true);
      reportTypeSelect.setNullValid(true);
      searchForm.add(reportTypeSelect);
    }
  }
}
/**
 * Helper class that allows putting (almost) arbitrary objects into Activiti processes.
 *
 * <p>Generally, prism objects and containers and jaxb objects are stored in their XML form,
 * allowing for safe deserialization in potentially newer version of midpoint.
 *
 * <p>Other serializable items are stored as such.
 *
 * <p>There's a child class (JaxbValueContainer) that allows directly retrieving XML representation
 * of the object (if there's one).
 *
 * @author mederly
 */
public class SingleItemSerializationSafeContainerImpl<T> implements SerializationSafeContainer<T> {

  private static final long serialVersionUID = 7269803380754945968L;

  private static final Trace LOGGER =
      TraceManager.getTrace(SingleItemSerializationSafeContainerImpl.class);
  public static final int MAX_WIDTH = 500;

  // this is the actual (directly usable) value of the item
  private transient T actualValue;

  // if there's no need to encode the value, it is stored in the first attribute
  // if there is (e.g. for PrismObjects) the encoded value is stored in the second attribute
  private T valueForStorageWhenNotEncoded;
  protected String valueForStorageWhenEncoded;

  // beware, for JAXB, PRISM_OBJECT and PRISM_CONTAINER encoding schemes the value must be XML, as
  // it might be
  // exposed through JaxbValueContainer
  protected EncodingScheme encodingScheme;

  private transient PrismContext prismContext;

  public SingleItemSerializationSafeContainerImpl(T value, PrismContext prismContext) {
    Validate.notNull(prismContext, "prismContext must not be null");
    this.prismContext = prismContext;
    setValue(value);
  }

  @Override
  public void setValue(T value) {
    this.actualValue = value;

    checkPrismContext();
    if (value != null && prismContext.canSerialize(value)) {
      try {
        this.valueForStorageWhenEncoded =
            prismContext.xmlSerializer().serializeAnyData(value, new QName("value"));
      } catch (SchemaException e) {
        throw new SystemException(
            "Couldn't serialize value of type " + value.getClass() + ": " + e.getMessage(), e);
      }
      this.valueForStorageWhenNotEncoded = null;
      encodingScheme = EncodingScheme.PRISM;
    } else if (value == null || value instanceof Serializable) {
      this.valueForStorageWhenNotEncoded = value;
      this.valueForStorageWhenEncoded = null;
      encodingScheme = EncodingScheme.NONE;
      if (value instanceof Itemable) {
        throw new IllegalStateException(
            "Itemable value is used as not-encoded serializable item; value = " + value);
      }
    } else {
      throw new IllegalStateException(
          "Attempt to put non-serializable item "
              + value.getClass()
              + " into "
              + this.getClass().getSimpleName());
    }
  }

  private void checkPrismContext() {
    Validate.notNull(prismContext, "In SerializationSafeContainer the prismContext is not set up");
  }

  @Override
  public T getValue() {

    if (actualValue != null) {
      return actualValue;
    }

    if (valueForStorageWhenNotEncoded != null) {
      actualValue = valueForStorageWhenNotEncoded;
      return actualValue;
    }

    if (valueForStorageWhenEncoded != null) {
      if (prismContext == null) {
        throw new IllegalStateException(
            "PrismContext not set for SerializationSafeContainer holding "
                + StringUtils.abbreviate(valueForStorageWhenEncoded, MAX_WIDTH));
      }

      if (encodingScheme == EncodingScheme.PRISM) {
        try {
          PrismValue prismValue =
              prismContext.parserFor(valueForStorageWhenEncoded).xml().parseItemValue();
          actualValue = prismValue != null ? prismValue.getRealValue() : null;
        } catch (SchemaException e) {
          throw new SystemException(
              "Couldn't deserialize value from JAXB: "
                  + StringUtils.abbreviate(valueForStorageWhenEncoded, MAX_WIDTH),
              e);
        }
        return actualValue;
      } else {
        throw new IllegalStateException("Unexpected encoding scheme " + encodingScheme);
      }
    }

    return null;
  }

  @Override
  public PrismContext getPrismContext() {
    return prismContext;
  }

  @Override
  public void setPrismContext(PrismContext prismContext) {
    this.prismContext = prismContext;
  }

  // for testing purposes
  @Override
  public void clearActualValue() {
    actualValue = null;
  }

  public enum EncodingScheme {
    PRISM,
    NONE
  };

  @Override
  public String toString() {
    return "SerializationSafeContainer{"
        + "actualValue "
        + (actualValue != null ? "SET" : "NOT SET")
        + ", valueForStorageWhenNotEncoded="
        + valueForStorageWhenNotEncoded
        + ", valueForStorageWhenEncoded='"
        + valueForStorageWhenEncoded
        + '\''
        + ", encodingScheme="
        + encodingScheme
        + ", prismContext "
        + (prismContext != null ? "SET" : "NOT SET")
        + '}';
  }

  @Override
  public String debugDump(int indent) {
    StringBuilder sb = new StringBuilder();
    if (actualValue != null) {
      debugDumpValue(indent, sb, actualValue);
    } else if (valueForStorageWhenNotEncoded != null) {
      debugDumpValue(indent, sb, valueForStorageWhenNotEncoded);
    } else if (valueForStorageWhenEncoded != null) {
      DebugUtil.debugDumpWithLabel(sb, "encoded value", valueForStorageWhenEncoded, indent);
    } else {
      DebugUtil.debugDumpWithLabel(sb, "value", "null", indent);
    }
    return sb.toString();
  }

  private void debugDumpValue(int indent, StringBuilder sb, T value) {
    if (value instanceof DebugDumpable) {
      DebugUtil.debugDumpWithLabel(sb, "value", (DebugDumpable) value, indent);
      return;
    }
    String stringValue = null;
    if (value instanceof ExpressionType) {
      // brutal hack...
      String xml = null;
      try {
        xml =
            prismContext
                .xmlSerializer()
                .serializeRealValue(value, SchemaConstantsGenerated.C_EXPRESSION);
        stringValue = DebugUtil.fixIndentInMultiline(indent, DebugDumpable.INDENT_STRING, xml);
      } catch (SchemaException e) {
        LOGGER.warn("Couldn't serialize an expression: {}", value, e);
      }
    }
    if (stringValue == null) {
      stringValue = String.valueOf(value);
    }
    DebugUtil.debugDumpWithLabel(sb, "value", stringValue, indent);
  }
}
/** @author mederly */
@Component
public class GeneralChangeProcessor extends BaseChangeProcessor {

  private static final Trace LOGGER = TraceManager.getTrace(GeneralChangeProcessor.class);

  @Autowired private PrismContext prismContext;

  @Autowired private WfTaskUtil wfTaskUtil;

  @Autowired private JobController jobController;

  @Autowired private ActivitiEngine activitiEngine;

  @Autowired private BaseModelInvocationProcessingHelper baseModelInvocationProcessingHelper;

  @Autowired private BaseExternalizationHelper baseExternalizationHelper;

  @Autowired private BaseAuditHelper baseAuditHelper;

  @Autowired private GcpConfigurationHelper gcpConfigurationHelper;

  @Autowired private GcpExpressionHelper gcpExpressionHelper;

  @Autowired private GcpExternalizationHelper gcpExternalizationHelper;

  private GeneralChangeProcessorConfigurationType processorConfigurationType;

  // region Initialization and Configuration
  @PostConstruct
  public void init() {
    processorConfigurationType = gcpConfigurationHelper.configure(this);
    if (isEnabled()) {
      // print startup message
      int scenarios = processorConfigurationType.getScenario().size();
      if (scenarios > 0) {
        LOGGER.info(
            getBeanName() + " initialized correctly (number of scenarios: " + scenarios + ")");
      } else {
        LOGGER.warn(
            getBeanName()
                + " initialized correctly, but there are no scenarios - so it will never be invoked");
      }
    }
  }

  private GeneralChangeProcessorScenarioType findScenario(String scenarioName) {
    for (GeneralChangeProcessorScenarioType scenario : processorConfigurationType.getScenario()) {
      if (scenarioName.equals(scenario.getName())) {
        return scenario;
      }
    }
    throw new SystemException("Scenario named " + scenarioName + " couldn't be found");
  }

  private GcpScenarioBean getScenarioBean(Map<String, Object> variables) {
    String beanName =
        (String) variables.get(GcpProcessVariableNames.VARIABLE_MIDPOINT_SCENARIO_BEAN_NAME);
    return findScenarioBean(beanName);
  }

  public GcpScenarioBean findScenarioBean(String name) {
    if (name == null) {
      name = lowerFirstChar(DefaultGcpScenarioBean.class.getSimpleName());
    }
    if (getBeanFactory().containsBean(name)) {
      return getBeanFactory().getBean(name, GcpScenarioBean.class);
    } else {
      throw new IllegalStateException("Scenario bean " + name + " couldn't be found.");
    }
  }

  private String lowerFirstChar(String name) {
    return Character.toLowerCase(name.charAt(0)) + name.substring(1);
  }

  public void disableScenario(String scenarioName) {
    findScenario(scenarioName).setEnabled(false);
  }

  public void enableScenario(String scenarioName) {
    findScenario(scenarioName).setEnabled(true);
  }
  // endregion

  // region Processing model invocation
  @Override
  public HookOperationMode processModelInvocation(
      ModelContext context, Task taskFromModel, OperationResult result) throws SchemaException {

    if (processorConfigurationType.getScenario().isEmpty()) {
      LOGGER.warn("No scenarios for " + getBeanName());
    }

    for (GeneralChangeProcessorScenarioType scenarioType :
        processorConfigurationType.getScenario()) {
      GcpScenarioBean scenarioBean = findScenarioBean(scenarioType.getBeanName());
      if (Boolean.FALSE.equals(scenarioType.isEnabled())) {
        LOGGER.trace("scenario {} is disabled, skipping", scenarioType.getName());
      } else if (!gcpExpressionHelper.evaluateActivationCondition(
          scenarioType, context, taskFromModel, result)) {
        LOGGER.trace(
            "activationCondition was evaluated to FALSE for scenario named {}",
            scenarioType.getName());
      } else if (!scenarioBean.determineActivation(scenarioType, context, taskFromModel, result)) {
        LOGGER.trace("scenarioBean decided to skip scenario named {}", scenarioType.getName());
      } else {
        LOGGER.trace(
            "Applying scenario {} (process name {})",
            scenarioType.getName(),
            scenarioType.getProcessName());
        return applyScenario(scenarioType, scenarioBean, context, taskFromModel, result);
      }
    }
    LOGGER.trace("No scenario found to be applicable, exiting the change processor.");
    return HookOperationMode.FOREGROUND;
  }

  private HookOperationMode applyScenario(
      GeneralChangeProcessorScenarioType scenarioType,
      GcpScenarioBean scenarioBean,
      ModelContext context,
      Task taskFromModel,
      OperationResult result) {

    try {
      // ========== preparing root task ===========

      JobCreationInstruction rootInstruction =
          baseModelInvocationProcessingHelper.createInstructionForRoot(
              this, context, taskFromModel);
      Job rootJob =
          baseModelInvocationProcessingHelper.createRootJob(rootInstruction, taskFromModel, result);

      // ========== preparing child task, starting WF process ===========

      JobCreationInstruction instruction =
          scenarioBean.prepareJobCreationInstruction(
              scenarioType, (LensContext<?>) context, rootJob, taskFromModel, result);
      jobController.createJob(instruction, rootJob, result);

      // ========== complete the action ===========

      baseModelInvocationProcessingHelper.logJobsBeforeStart(rootJob, result);
      rootJob.startWaitingForSubtasks(result);

      return HookOperationMode.BACKGROUND;

    } catch (SchemaException
        | ObjectNotFoundException
        | CommunicationException
        | ConfigurationException
        | RuntimeException e) {
      LoggingUtils.logException(LOGGER, "Workflow process(es) could not be started", e);
      result.recordFatalError("Workflow process(es) could not be started: " + e, e);
      return HookOperationMode.ERROR;
      // todo rollback - at least close open tasks, maybe stop workflow process instances
    }
  }
  // endregion

  // region Finalizing the processing
  @Override
  public void onProcessEnd(ProcessEvent event, Job job, OperationResult result)
      throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException {

    Task task = job.getTask();
    // we simply put model context back into parent task
    // (or if it is null, we set the task to skip model context processing)

    // it is safe to directly access the parent, because (1) it is in waiting state, (2) we are its
    // only child

    Task rootTask = task.getParentTask(result);

    SerializationSafeContainer<LensContextType> contextContainer =
        (SerializationSafeContainer<LensContextType>)
            event.getVariable(GcpProcessVariableNames.VARIABLE_MODEL_CONTEXT);
    LensContextType lensContextType = null;
    if (contextContainer != null) {
      contextContainer.setPrismContext(prismContext);
      lensContextType = contextContainer.getValue();
    }

    if (lensContextType == null) {
      LOGGER.debug(
          GcpProcessVariableNames.VARIABLE_MODEL_CONTEXT
              + " not present in process, this means we should stop processing. Task = {}",
          rootTask);
      wfTaskUtil.setSkipModelContextProcessingProperty(rootTask, true, result);
    } else {
      LOGGER.debug(
          "Putting (changed or unchanged) value of {} into the task {}",
          GcpProcessVariableNames.VARIABLE_MODEL_CONTEXT,
          rootTask);
      wfTaskUtil.storeModelContext(
          rootTask, lensContextType.asPrismContainerValue().getContainer());
    }

    rootTask.savePendingModifications(result);
    LOGGER.trace("onProcessEnd ending for task {}", task);
  }
  // endregion

  // region Externalization methods (including auditing)
  @Override
  public PrismObject<? extends WorkItemContents> externalizeWorkItemContents(
      org.activiti.engine.task.Task task,
      Map<String, Object> processInstanceVariables,
      OperationResult result)
      throws JAXBException, ObjectNotFoundException, SchemaException {
    return getScenarioBean(processInstanceVariables)
        .externalizeWorkItemContents(task, processInstanceVariables, result);
  }

  @Override
  public PrismObject<? extends ProcessInstanceState> externalizeProcessInstanceState(
      Map<String, Object> variables) throws JAXBException, SchemaException {
    PrismObject<ProcessInstanceState> state = baseExternalizationHelper.externalizeState(variables);
    ProcessSpecificState processSpecificState =
        getScenarioBean(variables).externalizeInstanceState(variables);
    state.asObjectable().setProcessSpecificState(processSpecificState);
    return state;
  }

  @Override
  public AuditEventRecord prepareProcessInstanceAuditRecord(
      Map<String, Object> variables, Job job, AuditEventStage stage, OperationResult result) {
    return getScenarioBean(variables)
        .prepareProcessInstanceAuditRecord(variables, job, stage, result);
  }

  @Override
  public AuditEventRecord prepareWorkItemAuditRecord(
      TaskEvent taskEvent, AuditEventStage stage, OperationResult result) throws WorkflowException {
    return getScenarioBean(taskEvent.getVariables())
        .prepareWorkItemAuditRecord(taskEvent, stage, result);
  }
  // endregion
}
Example #18
0
/**
 * Interprets midPoint queries by translating them to hibernate (HQL) ones.
 *
 * <p>There are two parts: - filter translation, - paging translation.
 *
 * <p>As for filter translation, we traverse the filter depth-first, creating an isomorphic
 * structure of Restrictions. While creating them, we continually build a set of entity references
 * that are necessary to evaluate the query; these references are in the form of cartesian join of
 * entities from which each one can have a set of entities connected to it via left outer join. An
 * example:
 *
 * <p>from RUser u left join u.assignments a with ... left join u.organization o, RRole r left join
 * r.assignments a2 with ...
 *
 * <p>This structure is maintained in InterpretationContext, namely in the HibernateQuery being
 * prepared. (In order to produce HQL, we use ad-hoc "hibernate query model" in hqm package, rooted
 * in HibernateQuery class.)
 *
 * <p>Paging translation is done after filters are translated. It may add some entity references as
 * well, if they are not already present.
 *
 * @author lazyman
 * @author mederly
 */
public class QueryInterpreter2 {

  private static final Trace LOGGER = TraceManager.getTrace(QueryInterpreter2.class);
  private static final Map<Class, Matcher> AVAILABLE_MATCHERS;

  static {
    Map<Class, Matcher> matchers = new HashMap<>();
    // default matcher with null key
    matchers.put(null, new DefaultMatcher());
    matchers.put(PolyString.class, new PolyStringMatcher());
    matchers.put(String.class, new StringMatcher());

    AVAILABLE_MATCHERS = Collections.unmodifiableMap(matchers);
  }

  private SqlRepositoryConfiguration repoConfiguration;

  public QueryInterpreter2(SqlRepositoryConfiguration repoConfiguration) {
    this.repoConfiguration = repoConfiguration;
  }

  public SqlRepositoryConfiguration getRepoConfiguration() {
    return repoConfiguration;
  }

  public RootHibernateQuery interpret(
      ObjectQuery query,
      Class<? extends Containerable> type,
      Collection<SelectorOptions<GetOperationOptions>> options,
      PrismContext prismContext,
      boolean countingObjects,
      Session session)
      throws QueryException {
    Validate.notNull(type, "Type must not be null.");
    Validate.notNull(session, "Session must not be null.");
    Validate.notNull(prismContext, "Prism context must not be null.");

    if (LOGGER.isTraceEnabled()) {
      LOGGER.trace("Interpreting query for type '{}', query:\n{}", new Object[] {type, query});
    }

    InterpretationContext context = new InterpretationContext(this, type, prismContext, session);

    interpretQueryFilter(context, query);
    interpretPagingAndSorting(context, query, countingObjects);

    RootHibernateQuery hibernateQuery = context.getHibernateQuery();

    if (countingObjects) {
      hibernateQuery.addProjectionElement(new ProjectionElement("count(*)"));
    } else {
      String rootAlias = hibernateQuery.getPrimaryEntityAlias();
      hibernateQuery.addProjectionElement(new ProjectionElement(rootAlias + ".fullObject"));
      // TODO other objects if parent is requested?
      if (context.isObject()) {
        hibernateQuery.addProjectionElement(new ProjectionElement(rootAlias + ".stringsCount"));
        hibernateQuery.addProjectionElement(new ProjectionElement(rootAlias + ".longsCount"));
        hibernateQuery.addProjectionElement(new ProjectionElement(rootAlias + ".datesCount"));
        hibernateQuery.addProjectionElement(new ProjectionElement(rootAlias + ".referencesCount"));
        hibernateQuery.addProjectionElement(new ProjectionElement(rootAlias + ".polysCount"));
        hibernateQuery.addProjectionElement(new ProjectionElement(rootAlias + ".booleansCount"));
        hibernateQuery.setResultTransformer(GetObjectResult.RESULT_TRANSFORMER);
      } else {
        hibernateQuery.addProjectionElement(new ProjectionElement(rootAlias + ".ownerOid"));
        hibernateQuery.setResultTransformer(GetContainerableResult.RESULT_TRANSFORMER);
      }
    }

    return hibernateQuery;
  }

  private void interpretQueryFilter(InterpretationContext context, ObjectQuery query)
      throws QueryException {
    if (query != null && query.getFilter() != null) {
      Condition c = interpretFilter(context, query.getFilter(), null);
      context.getHibernateQuery().addCondition(c);
    }
  }

  public Condition interpretFilter(
      InterpretationContext context, ObjectFilter filter, Restriction parent)
      throws QueryException {
    Restriction restriction = findAndCreateRestriction(filter, context, parent);
    Condition condition = restriction.interpret();
    return condition;
  }

  private <T extends ObjectFilter> Restriction findAndCreateRestriction(
      T filter, InterpretationContext context, Restriction parent) throws QueryException {

    Validate.notNull(filter, "filter");
    Validate.notNull(context, "context");

    LOGGER.trace("Determining restriction for filter {}", filter);

    ItemPathResolver helper = context.getItemPathResolver();
    JpaEntityDefinition baseEntityDefinition;
    if (parent != null) {
      baseEntityDefinition = parent.getBaseHqlEntityForChildren().getJpaDefinition();
    } else {
      baseEntityDefinition = context.getRootEntityDefinition();
    }
    Restriction restriction =
        findAndCreateRestrictionInternal(filter, context, parent, helper, baseEntityDefinition);

    LOGGER.trace("Restriction for {} is {}", filter.getClass().getSimpleName(), restriction);
    return restriction;
  }

  private <T extends ObjectFilter> Restriction findAndCreateRestrictionInternal(
      T filter,
      InterpretationContext context,
      Restriction parent,
      ItemPathResolver resolver,
      JpaEntityDefinition baseEntityDefinition)
      throws QueryException {

    // the order of processing restrictions can be important, so we do the selection via handwritten
    // code

    if (filter instanceof AndFilter) {
      return new AndRestriction(context, (AndFilter) filter, baseEntityDefinition, parent);
    } else if (filter instanceof OrFilter) {
      return new OrRestriction(context, (OrFilter) filter, baseEntityDefinition, parent);
    } else if (filter instanceof NotFilter) {
      return new NotRestriction(context, (NotFilter) filter, baseEntityDefinition, parent);
    } else if (filter instanceof InOidFilter) {
      return new InOidRestriction(context, (InOidFilter) filter, baseEntityDefinition, parent);
    } else if (filter instanceof OrgFilter) {
      return new OrgRestriction(context, (OrgFilter) filter, baseEntityDefinition, parent);
    } else if (filter instanceof TypeFilter) {
      TypeFilter typeFilter = (TypeFilter) filter;
      JpaEntityDefinition refinedEntityDefinition =
          resolver.findRestrictedEntityDefinition(baseEntityDefinition, typeFilter.getType());
      return new TypeRestriction(context, typeFilter, refinedEntityDefinition, parent);
    } else if (filter instanceof ExistsFilter) {
      ExistsFilter existsFilter = (ExistsFilter) filter;
      ItemPath path = existsFilter.getFullPath();
      ItemDefinition definition = existsFilter.getDefinition();
      ProperDataSearchResult<JpaEntityDefinition> searchResult =
          resolver.findProperDataDefinition(
              baseEntityDefinition, path, definition, JpaEntityDefinition.class);
      if (searchResult == null) {
        throw new QueryException(
            "Path for ExistsFilter ("
                + path
                + ") doesn't point to a hibernate entity within "
                + baseEntityDefinition);
      }
      return new ExistsRestriction(
          context, existsFilter, searchResult.getEntityDefinition(), parent);
    } else if (filter instanceof RefFilter) {
      RefFilter refFilter = (RefFilter) filter;
      ItemPath path = refFilter.getFullPath();
      ItemDefinition definition = refFilter.getDefinition();
      ProperDataSearchResult<JpaReferenceDefinition> searchResult =
          resolver.findProperDataDefinition(
              baseEntityDefinition, path, definition, JpaReferenceDefinition.class);
      if (searchResult == null) {
        throw new QueryException(
            "Path for RefFilter ("
                + path
                + ") doesn't point to a reference item within "
                + baseEntityDefinition);
      }
      return new ReferenceRestriction(
          context,
          refFilter,
          searchResult.getEntityDefinition(),
          parent,
          searchResult.getLinkDefinition());
    } else if (filter instanceof PropertyValueFilter) {
      PropertyValueFilter valFilter = (PropertyValueFilter) filter;
      ItemPath path = valFilter.getFullPath();
      ItemDefinition definition = valFilter.getDefinition();

      ProperDataSearchResult<JpaPropertyDefinition> propDefRes =
          resolver.findProperDataDefinition(
              baseEntityDefinition, path, definition, JpaPropertyDefinition.class);
      if (propDefRes == null) {
        throw new QueryException(
            "Couldn't find a proper restriction for a ValueFilter: " + valFilter.debugDump());
      }
      // TODO can't be unified?
      if (propDefRes.getTargetDefinition() instanceof JpaAnyPropertyDefinition) {
        return new AnyPropertyRestriction(
            context,
            valFilter,
            propDefRes.getEntityDefinition(),
            parent,
            propDefRes.getLinkDefinition());
      } else {
        return new PropertyRestriction(
            context,
            valFilter,
            propDefRes.getEntityDefinition(),
            parent,
            propDefRes.getLinkDefinition());
      }
    } else if (filter instanceof NoneFilter
        || filter instanceof AllFilter
        || filter instanceof UndefinedFilter) {
      // these should be filtered out by the client
      throw new IllegalStateException(
          "Trivial filters are not supported by QueryInterpreter: " + filter.debugDump());
    } else {
      throw new IllegalStateException("Unknown filter: " + filter.debugDump());
    }
  }

  private void interpretPagingAndSorting(
      InterpretationContext context, ObjectQuery query, boolean countingObjects)
      throws QueryException {
    RootHibernateQuery hibernateQuery = context.getHibernateQuery();
    String rootAlias = hibernateQuery.getPrimaryEntityAlias();

    if (query != null && query.getPaging() instanceof ObjectPagingAfterOid) {
      ObjectPagingAfterOid paging = (ObjectPagingAfterOid) query.getPaging();
      if (paging.getOidGreaterThan() != null) {
        Condition c =
            hibernateQuery.createSimpleComparisonCondition(
                rootAlias + ".oid", paging.getOidGreaterThan(), ">");
        hibernateQuery.addCondition(c);
      }
    }

    if (!countingObjects && query != null && query.getPaging() != null) {
      if (query.getPaging() instanceof ObjectPagingAfterOid) {
        updatePagingAndSortingByOid(
            hibernateQuery,
            (ObjectPagingAfterOid)
                query.getPaging()); // very special case - ascending ordering by OID (nothing more)
      } else {
        updatePagingAndSorting(context, query.getPaging());
      }
    }
  }

  protected void updatePagingAndSortingByOid(
      RootHibernateQuery hibernateQuery, ObjectPagingAfterOid paging) {
    String rootAlias = hibernateQuery.getPrimaryEntityAlias();
    if (paging.getOrderBy() != null
        || paging.getDirection() != null
        || paging.getOffset() != null) {
      throw new IllegalArgumentException(
          "orderBy, direction nor offset is allowed on ObjectPagingAfterOid");
    }
    hibernateQuery.addOrdering(rootAlias + ".oid", OrderDirection.ASCENDING);
    if (paging.getMaxSize() != null) {
      hibernateQuery.setMaxResults(paging.getMaxSize());
    }
  }

  public <T extends Containerable> void updatePagingAndSorting(
      InterpretationContext context, ObjectPaging paging) throws QueryException {

    if (paging == null) {
      return;
    }

    RootHibernateQuery hibernateQuery = context.getHibernateQuery();
    if (paging.getOffset() != null) {
      hibernateQuery.setFirstResult(paging.getOffset());
    }
    if (paging.getMaxSize() != null) {
      hibernateQuery.setMaxResults(paging.getMaxSize());
    }

    if (!paging.hasOrdering()) {
      return;
    }

    for (ObjectOrdering ordering : paging.getOrderingInstructions()) {
      addOrdering(context, ordering);
    }
  }

  private void addOrdering(InterpretationContext context, ObjectOrdering ordering)
      throws QueryException {

    ItemPath orderByPath = ordering.getOrderBy();

    // TODO if we'd like to have order-by extension properties, we'd need to provide itemDefinition
    // for them
    ProperDataSearchResult<JpaDataNodeDefinition> result =
        context
            .getItemPathResolver()
            .findProperDataDefinition(
                context.getRootEntityDefinition(), orderByPath, null, JpaDataNodeDefinition.class);
    if (result == null) {
      LOGGER.error(
          "Unknown path '"
              + orderByPath
              + "', couldn't find definition for it, "
              + "list will not be ordered by it.");
      return;
    }
    JpaDataNodeDefinition targetDefinition = result.getLinkDefinition().getTargetDefinition();
    if (targetDefinition instanceof JpaAnyContainerDefinition) {
      throw new QueryException(
          "Sorting based on extension item or attribute is not supported yet: " + orderByPath);
    } else if (targetDefinition instanceof JpaReferenceDefinition) {
      throw new QueryException("Sorting based on reference is not supported: " + orderByPath);
    } else if (result.getLinkDefinition().isMultivalued()) {
      throw new QueryException(
          "Sorting based on multi-valued item is not supported: " + orderByPath);
    } else if (targetDefinition instanceof JpaEntityDefinition) {
      throw new QueryException("Sorting based on entity is not supported: " + orderByPath);
    } else if (!(targetDefinition instanceof JpaPropertyDefinition)) {
      throw new IllegalStateException("Unknown item definition type: " + result.getClass());
    }

    JpaEntityDefinition baseEntityDefinition = result.getEntityDefinition();
    JpaPropertyDefinition orderByDefinition = (JpaPropertyDefinition) targetDefinition;
    String hqlPropertyPath =
        context
            .getItemPathResolver()
            .resolveItemPath(
                orderByPath, null, context.getPrimaryEntityAlias(), baseEntityDefinition, true)
            .getHqlPath();
    if (RPolyString.class.equals(orderByDefinition.getJpaClass())) {
      hqlPropertyPath += ".orig";
    }

    RootHibernateQuery hibernateQuery = context.getHibernateQuery();
    if (ordering.getDirection() != null) {
      switch (ordering.getDirection()) {
        case ASCENDING:
          hibernateQuery.addOrdering(hqlPropertyPath, OrderDirection.ASCENDING);
          break;
        case DESCENDING:
          hibernateQuery.addOrdering(hqlPropertyPath, OrderDirection.DESCENDING);
          break;
      }
    } else {
      hibernateQuery.addOrdering(hqlPropertyPath, OrderDirection.ASCENDING);
    }
  }

  public <T extends Object> Matcher<T> findMatcher(T value) {
    return findMatcher(value != null ? (Class<T>) value.getClass() : null);
  }

  public <T extends Object> Matcher<T> findMatcher(Class<T> type) {
    Matcher<T> matcher = AVAILABLE_MATCHERS.get(type);
    if (matcher == null) {
      // we return default matcher
      matcher = AVAILABLE_MATCHERS.get(null);
    }
    return matcher;
  }
}
/** @author shood */
public class ResourceDependencyEditor extends SimplePanel<List<ResourceObjectTypeDependencyType>> {

  private static enum ChangeState {
    SKIP,
    FIRST,
    LAST
  }

  private static final Trace LOGGER = TraceManager.getTrace(ResourceDependencyEditor.class);

  private static final String DOT_CLASS = ResourceDependencyEditor.class.getName() + ".";
  private static final String OPERATION_LOAD_RESOURCES = DOT_CLASS + "createResourceList";

  private static final String ID_CONTAINER = "protectedContainer";
  private static final String ID_REPEATER = "repeater";
  private static final String ID_DEPENDENCY_LINK = "dependencyLink";
  private static final String ID_DEPENDENCY_LINK_NAME = "dependencyLinkName";
  private static final String ID_DEPENDENCY_BODY = "dependencyBodyContainer";
  private static final String ID_ORDER = "order";
  private static final String ID_STRICTNESS = "strictness";
  private static final String ID_KIND = "kind";
  private static final String ID_INTENT = "intent";
  private static final String ID_REF = "resourceRef";
  private static final String ID_ADD_BUTTON = "addButton";
  private static final String ID_DELETE_BUTTON = "deleteDependency";
  private static final String ID_T_ORDER = "orderTooltip";
  private static final String ID_T_STRICTNESS = "strictnessTooltip";
  private static final String ID_T_KIND = "kindTooltip";
  private static final String ID_T_INTENT = "intentTooltip";
  private static final String ID_T_RESOURCE_REF = "resourceRefTooltip";

  private ChangeState changeState = ChangeState.FIRST;
  private Map<String, String> resourceMap = new HashMap<>();

  public ResourceDependencyEditor(String id, IModel<List<ResourceObjectTypeDependencyType>> model) {
    super(id, model);
  }

  @Override
  public IModel<List<ResourceObjectTypeDependencyType>> getModel() {
    IModel<List<ResourceObjectTypeDependencyType>> model = super.getModel();

    if (model.getObject() == null) {
      model.setObject(new ArrayList<ResourceObjectTypeDependencyType>());
    }

    return model;
  }

  @Override
  protected void initLayout() {
    WebMarkupContainer container = new WebMarkupContainer(ID_CONTAINER);
    container.setOutputMarkupId(true);
    add(container);

    ListView repeater =
        new ListView<ResourceObjectTypeDependencyType>(ID_REPEATER, getModel()) {

          @Override
          protected void populateItem(final ListItem<ResourceObjectTypeDependencyType> item) {
            WebMarkupContainer linkContainer = new WebMarkupContainer(ID_DEPENDENCY_LINK);
            linkContainer.setOutputMarkupId(true);
            linkContainer.add(new AttributeModifier("href", createCollapseItemId(item, true)));
            item.add(linkContainer);

            Label linkLabel = new Label(ID_DEPENDENCY_LINK_NAME, createDependencyLabelModel(item));
            linkContainer.add(linkLabel);

            AjaxLink delete =
                new AjaxLink(ID_DELETE_BUTTON) {

                  @Override
                  public void onClick(AjaxRequestTarget target) {
                    deleteDependencyPerformed(target, item);
                  }
                };
            linkContainer.add(delete);

            WebMarkupContainer dependencyBody = new WebMarkupContainer(ID_DEPENDENCY_BODY);
            dependencyBody.setOutputMarkupId(true);
            dependencyBody.setMarkupId(createCollapseItemId(item, false).getObject());

            if (changeState != ChangeState.SKIP) {
              dependencyBody.add(
                  new AttributeModifier(
                      "class",
                      new AbstractReadOnlyModel<String>() {

                        @Override
                        public String getObject() {
                          if (changeState == ChangeState.FIRST && item.getIndex() == 0) {
                            return "panel-collapse collapse in";
                          } else if (changeState == ChangeState.LAST
                              && item.getIndex() == (getModelObject().size() - 1)) {
                            return "panel-collapse collapse in";
                          } else {
                            return "panel-collapse collapse";
                          }
                        }
                      }));
            }

            item.add(dependencyBody);

            TextField order =
                new TextField<>(
                    ID_ORDER, new PropertyModel<Integer>(item.getModelObject(), "order"));
            order.add(prepareAjaxOnComponentTagUpdateBehavior());
            dependencyBody.add(order);

            DropDownChoice strictness =
                new DropDownChoice<>(
                    ID_STRICTNESS,
                    new PropertyModel<ResourceObjectTypeDependencyStrictnessType>(
                        item.getModelObject(), "strictness"),
                    WebMiscUtil.createReadonlyModelFromEnum(
                        ResourceObjectTypeDependencyStrictnessType.class),
                    new EnumChoiceRenderer<ResourceObjectTypeDependencyStrictnessType>(this));
            strictness.add(prepareAjaxOnComponentTagUpdateBehavior());
            dependencyBody.add(strictness);

            DropDownChoice kind =
                new DropDownChoice<>(
                    ID_KIND,
                    new PropertyModel<ShadowKindType>(item.getModelObject(), "kind"),
                    WebMiscUtil.createReadonlyModelFromEnum(ShadowKindType.class),
                    new EnumChoiceRenderer<ShadowKindType>(this));
            kind.add(prepareAjaxOnComponentTagUpdateBehavior());
            dependencyBody.add(kind);

            TextField intent =
                new TextField<>(
                    ID_INTENT, new PropertyModel<String>(item.getModelObject(), "intent"));
            intent.add(prepareAjaxOnComponentTagUpdateBehavior());
            dependencyBody.add(intent);

            DropDownChoice resource =
                new DropDownChoice<>(
                    ID_REF,
                    new PropertyModel<ObjectReferenceType>(item.getModelObject(), "resourceRef"),
                    new AbstractReadOnlyModel<List<ObjectReferenceType>>() {

                      @Override
                      public List<ObjectReferenceType> getObject() {
                        return createResourceList();
                      }
                    },
                    new IChoiceRenderer<ObjectReferenceType>() {

                      @Override
                      public Object getDisplayValue(ObjectReferenceType object) {
                        return createResourceReadLabel(object);
                      }

                      @Override
                      public String getIdValue(ObjectReferenceType object, int index) {
                        return Integer.toString(index);
                      }
                    });
            resource.add(prepareAjaxOnComponentTagUpdateBehavior());
            dependencyBody.add(resource);

            Label orderTooltip = new Label(ID_T_ORDER);
            orderTooltip.add(new InfoTooltipBehavior());
            dependencyBody.add(orderTooltip);

            Label strictnessTooltip = new Label(ID_T_STRICTNESS);
            strictnessTooltip.add(new InfoTooltipBehavior());
            dependencyBody.add(strictnessTooltip);

            Label kindTooltip = new Label(ID_T_KIND);
            kindTooltip.add(new InfoTooltipBehavior());
            dependencyBody.add(kindTooltip);

            Label intentTooltip = new Label(ID_T_INTENT);
            intentTooltip.add(new InfoTooltipBehavior());
            dependencyBody.add(intentTooltip);

            Label resourceRefTooltip = new Label(ID_T_RESOURCE_REF);
            resourceRefTooltip.add(new InfoTooltipBehavior());
            dependencyBody.add(resourceRefTooltip);
          }
        };
    repeater.setOutputMarkupId(true);
    container.add(repeater);

    AjaxLink add =
        new AjaxLink(ID_ADD_BUTTON) {

          @Override
          public void onClick(AjaxRequestTarget target) {
            addDependencyPerformed(target);
          }
        };
    add(add);
  }

  private IModel<String> createDependencyLabelModel(
      final ListItem<ResourceObjectTypeDependencyType> item) {
    return new AbstractReadOnlyModel<String>() {

      @Override
      public String getObject() {
        StringBuilder sb = new StringBuilder();
        ResourceObjectTypeDependencyType dep = item.getModelObject();
        sb.append("#").append(item.getIndex() + 1).append(" - ");

        if (dep.getResourceRef() != null) {
          sb.append(resourceMap.get(dep.getResourceRef().getOid())).append(":");
        }

        if (dep.getKind() != null) {
          sb.append(dep.getKind().toString()).append(":");
        }

        if (dep.getIntent() != null) {
          sb.append(dep.getIntent()).append(":");
        }

        sb.append(dep.getOrder()).append(":");
        if (dep.getStrictness() != null) {
          sb.append(dep.getStrictness().toString());
        }

        return sb.toString();
      }
    };
  }

  private AjaxFormComponentUpdatingBehavior prepareAjaxOnComponentTagUpdateBehavior() {
    return new AjaxFormComponentUpdatingBehavior("onBlur") {

      @Override
      protected void onUpdate(AjaxRequestTarget target) {}
    };
  }

  private List<ObjectReferenceType> createResourceList() {
    resourceMap.clear();
    OperationResult result = new OperationResult(OPERATION_LOAD_RESOURCES);
    Task task = getPageBase().createSimpleTask(OPERATION_LOAD_RESOURCES);
    List<PrismObject<ResourceType>> resources = null;
    List<ObjectReferenceType> references = new ArrayList<>();

    try {
      resources =
          getPageBase()
              .getModelService()
              .searchObjects(ResourceType.class, new ObjectQuery(), null, task, result);
      result.recomputeStatus();
    } catch (Exception e) {
      result.recordFatalError("Couldn't get resource list.", e);
      LoggingUtils.logException(LOGGER, "Couldn't get resource list.", e);
    }

    // TODO - show error somehow
    // if(!result.isSuccess()){
    //    getPageBase().showResult(result);
    // }

    if (resources != null) {
      ObjectReferenceType ref;
      for (PrismObject<ResourceType> r : resources) {
        resourceMap.put(r.getOid(), WebMiscUtil.getName(r));
        ref = new ObjectReferenceType();
        ref.setType(ResourceType.COMPLEX_TYPE);
        ref.setOid(r.getOid());
        references.add(ref);
      }
    }

    return references;
  }

  private String createResourceReadLabel(ObjectReferenceType ref) {
    return resourceMap.get(ref.getOid());
  }

  private WebMarkupContainer getMainContainer() {
    return (WebMarkupContainer) get(ID_CONTAINER);
  }

  private IModel<String> createCollapseItemId(
      final ListItem<ResourceObjectTypeDependencyType> item, final boolean appendSelector) {
    return new AbstractReadOnlyModel<String>() {

      @Override
      public String getObject() {
        StringBuilder sb = new StringBuilder();

        if (appendSelector) {
          sb.append("#");
        }

        sb.append("collapse").append(item.getId());

        return sb.toString();
      }
    };
  }

  private void addDependencyPerformed(AjaxRequestTarget target) {
    ResourceObjectTypeDependencyType dependency = new ResourceObjectTypeDependencyType();
    changeState = ChangeState.LAST;
    getModel().getObject().add(dependency);
    target.add(getMainContainer());
  }

  private void deleteDependencyPerformed(
      AjaxRequestTarget target, ListItem<ResourceObjectTypeDependencyType> item) {
    changeState = ChangeState.SKIP;
    getModel().getObject().remove(item.getModelObject());
    target.add(getMainContainer());
  }
}
/**
 * Read-through write-through per-session repository cache.
 *
 * <p>TODO doc TODO logging perf measurements
 *
 * @author Radovan Semancik
 */
public class RepositoryCache implements RepositoryService {

  private static ThreadLocal<Cache> cacheInstance = new ThreadLocal<>();

  private RepositoryService repository;

  private static final Trace LOGGER = TraceManager.getTrace(RepositoryCache.class);
  private static final Trace PERFORMANCE_ADVISOR = TraceManager.getPerformanceAdvisorTrace();

  private PrismContext prismContext;

  public RepositoryCache() {}

  public void setRepository(RepositoryService service, PrismContext prismContext) {
    Validate.notNull(service, "Repository service must not be null.");
    Validate.notNull(prismContext, "Prism context service must not be null.");
    this.repository = service;
    this.prismContext = prismContext;
  }

  private static Cache getCache() {
    return cacheInstance.get();
  }

  public static void init() {}

  public static void destroy() {
    Cache.destroy(cacheInstance, LOGGER);
  }

  public static void enter() {
    Cache.enter(cacheInstance, Cache.class, LOGGER);
  }

  public static void exit() {
    Cache.exit(cacheInstance, LOGGER);
  }

  public static boolean exists() {
    return Cache.exists(cacheInstance);
  }

  public static String debugDump() {
    return Cache.debugDump(cacheInstance);
  }

  @Override
  public <T extends ObjectType> PrismObject<T> getObject(
      Class<T> type,
      String oid,
      Collection<SelectorOptions<GetOperationOptions>> options,
      OperationResult parentResult)
      throws ObjectNotFoundException, SchemaException {
    if (!isCacheable(type) || !nullOrHarmlessOptions(options)) {
      log("Cache: PASS {} ({})", oid, type.getSimpleName());
      return repository.getObject(type, oid, options, parentResult);
    }
    Cache cache = getCache();
    if (cache == null) {
      log("Cache: NULL {} ({})", oid, type.getSimpleName());
    } else {
      PrismObject<T> object = (PrismObject) cache.getObject(oid);
      if (object != null) {
        // TODO: result?
        log("Cache: HIT {} ({})", oid, type.getSimpleName());
        return object.clone();
      }
      log("Cache: MISS {} ({})", oid, type.getSimpleName());
    }
    PrismObject<T> object = repository.getObject(type, oid, null, parentResult);
    cacheObject(cache, object);
    return object;
  }

  private boolean isCacheable(Class<?> type) {
    if (type.equals(TaskType.class)) {
      return false;
    }
    //		if (ShadowType.class.isAssignableFrom(type)) {
    //			return false;
    //		}
    return true;
  }

  @Override
  public <T extends ObjectType> String addObject(
      PrismObject<T> object, RepoAddOptions options, OperationResult parentResult)
      throws ObjectAlreadyExistsException, SchemaException {
    String oid = repository.addObject(object, options, parentResult);
    Cache cache = getCache();
    // DON't cache the object here. The object may not have proper "JAXB" form, e.g. some pieces may
    // be
    // DOM element instead of JAXB elements. Not to cache it is safer and the performance loss
    // is acceptable.
    if (cache != null) {
      // Invalidate the cache entry if it happens to be there
      cache.removeObject(oid);
      cache.clearQueryResults(object.getCompileTimeClass());
    }
    return oid;
  }

  @Override
  public <T extends ObjectType> SearchResultList<PrismObject<T>> searchObjects(
      Class<T> type,
      ObjectQuery query,
      Collection<SelectorOptions<GetOperationOptions>> options,
      OperationResult parentResult)
      throws SchemaException {
    if (!isCacheable(type) || !nullOrHarmlessOptions(options)) {
      log("Cache: PASS ({})", type.getSimpleName());
      return repository.searchObjects(type, query, options, parentResult);
    }
    Cache cache = getCache();
    if (cache == null) {
      log("Cache: NULL ({})", type.getSimpleName());
    } else {
      SearchResultList queryResult = cache.getQueryResult(type, query, prismContext);
      if (queryResult != null) {
        log("Cache: HIT {} ({})", query, type.getSimpleName());
        return queryResult.clone();
      }
      log("Cache: MISS {} ({})", query, type.getSimpleName());
    }

    // Cannot satisfy from cache, pass down to repository
    SearchResultList<PrismObject<T>> objects =
        repository.searchObjects(type, query, options, parentResult);
    if (cache != null && options == null) {
      for (PrismObject<T> object : objects) {
        cacheObject(cache, object);
      }
      cache.putQueryResult(type, query, objects, prismContext);
    }
    return objects;
  }

  /* (non-Javadoc)
   * @see com.evolveum.midpoint.repo.api.RepositoryService#searchObjectsIterative(java.lang.Class, com.evolveum.midpoint.prism.query.ObjectQuery, com.evolveum.midpoint.schema.ResultHandler, com.evolveum.midpoint.schema.result.OperationResult)
   */
  @Override
  public <T extends ObjectType> SearchResultMetadata searchObjectsIterative(
      Class<T> type,
      ObjectQuery query,
      final ResultHandler<T> handler,
      Collection<SelectorOptions<GetOperationOptions>> options,
      OperationResult parentResult)
      throws SchemaException {
    // TODO use cached query result if applicable
    log("Cache: PASS searchObjectsIterative ({})", type.getSimpleName());
    final Cache cache = getCache();
    ResultHandler<T> myHandler =
        new ResultHandler<T>() {
          @Override
          public boolean handle(PrismObject<T> object, OperationResult parentResult) {
            cacheObject(cache, object);
            return handler.handle(object, parentResult);
          }
        };
    return repository.searchObjectsIterative(type, query, myHandler, options, parentResult);
  }

  @Override
  public <T extends ObjectType> int countObjects(
      Class<T> type, ObjectQuery query, OperationResult parentResult) throws SchemaException {
    // TODO use cached query result if applicable
    log("Cache: PASS countObjects ({})", type.getSimpleName());
    return repository.countObjects(type, query, parentResult);
  }

  @Override
  public <T extends ObjectType> void modifyObject(
      Class<T> type,
      String oid,
      Collection<? extends ItemDelta> modifications,
      OperationResult parentResult)
      throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException {
    repository.modifyObject(type, oid, modifications, parentResult);
    // this changes the object. We are too lazy to apply changes ourselves, so just invalidate
    // the object in cache
    Cache cache = getCache();
    if (cache != null) {
      cache.removeObject(oid);
      cache.clearQueryResults(type);
    }
  }

  @Override
  public <T extends ObjectType> void deleteObject(
      Class<T> type, String oid, OperationResult parentResult) throws ObjectNotFoundException {
    repository.deleteObject(type, oid, parentResult);
    Cache cache = getCache();
    if (cache != null) {
      cache.removeObject(oid);
      cache.clearQueryResults(type);
    }
  }

  @Override
  public <F extends FocusType> PrismObject<F> searchShadowOwner(
      String shadowOid,
      Collection<SelectorOptions<GetOperationOptions>> options,
      OperationResult parentResult)
      throws ObjectNotFoundException {
    // TODO cache the search operation?
    PrismObject<F> ownerObject = repository.searchShadowOwner(shadowOid, options, parentResult);
    if (ownerObject != null && nullOrHarmlessOptions(options)) {
      cacheObject(getCache(), ownerObject);
    }
    return ownerObject;
  }

  private boolean nullOrHarmlessOptions(Collection<SelectorOptions<GetOperationOptions>> options) {
    if (options == null || options.isEmpty()) {
      return true;
    }
    if (options.size() > 1) {
      return false;
    }
    SelectorOptions<GetOperationOptions> selectorOptions = options.iterator().next();
    if (!selectorOptions.isRoot()) {
      return false;
    }
    GetOperationOptions options1 = selectorOptions.getOptions();
    if (options1 == null
        || options1.equals(new GetOperationOptions())
        || options1.equals(GetOperationOptions.createAllowNotFound())) {
      return true;
    }
    return false;
  }

  @Override
  @Deprecated
  public PrismObject<UserType> listAccountShadowOwner(
      String accountOid, OperationResult parentResult) throws ObjectNotFoundException {
    return repository.listAccountShadowOwner(accountOid, parentResult);
  }

  @Override
  public <T extends ShadowType> List<PrismObject<T>> listResourceObjectShadows(
      String resourceOid, Class<T> resourceObjectShadowType, OperationResult parentResult)
      throws ObjectNotFoundException, SchemaException {
    return repository.listResourceObjectShadows(
        resourceOid, resourceObjectShadowType, parentResult);
  }

  /* (non-Javadoc)
   * @see com.evolveum.midpoint.repo.api.RepositoryService#getVersion(java.lang.Class, java.lang.String, com.evolveum.midpoint.schema.result.OperationResult)
   */
  @Override
  public <T extends ObjectType> String getVersion(
      Class<T> type, String oid, OperationResult parentResult)
      throws ObjectNotFoundException, SchemaException {
    if (!isCacheable(type)) {
      log("Cache: PASS {} ({})", oid, type.getSimpleName());
      return repository.getVersion(type, oid, parentResult);
    }
    Cache cache = getCache();
    if (cache == null) {
      log("Cache: NULL {} ({})", oid, type.getSimpleName());
    } else {
      String version = cache.getObjectVersion(oid);
      if (version != null) {
        log("Cache: HIT {} ({})", oid, type.getSimpleName());
        return version;
      }
      log("Cache: MISS {} ({})", oid, type.getSimpleName());
    }
    String version = repository.getVersion(type, oid, parentResult);
    cacheObjectVersion(cache, oid, version);
    return version;
  }

  /* (non-Javadoc)
   * @see com.evolveum.midpoint.repo.api.RepositoryService#getRepositoryDiag()
   */
  @Override
  public RepositoryDiag getRepositoryDiag() {
    return repository.getRepositoryDiag();
  }

  /* (non-Javadoc)
   * @see com.evolveum.midpoint.repo.api.RepositoryService#repositorySelfTest(com.evolveum.midpoint.schema.result.OperationResult)
   */
  @Override
  public void repositorySelfTest(OperationResult parentResult) {
    repository.repositorySelfTest(parentResult);
  }

  @Override
  public void testOrgClosureConsistency(boolean repairIfNecessary, OperationResult testResult) {
    repository.testOrgClosureConsistency(repairIfNecessary, testResult);
  }

  private <T extends ObjectType> void cacheObject(Cache cache, PrismObject<T> object) {
    if (cache != null) {
      cache.putObject(object.getOid(), (PrismObject<ObjectType>) object.clone());
    }
  }

  private <T extends ObjectType> void cacheObjectVersion(Cache cache, String oid, String version) {
    if (cache != null) {
      cache.putObjectVersion(oid, version);
    }
  }

  @Override
  public boolean isAnySubordinate(String upperOrgOid, Collection<String> lowerObjectOids)
      throws SchemaException {
    return repository.isAnySubordinate(upperOrgOid, lowerObjectOids);
  }

  private void log(String message, Object... params) {
    if (LOGGER.isTraceEnabled()) {
      LOGGER.trace(message, params);
    }
    if (PERFORMANCE_ADVISOR.isTraceEnabled()) {
      PERFORMANCE_ADVISOR.trace(message, params);
    }
  }
}
/**
 * @author katkav
 * @author lazyman
 */
@PageDescriptor(
    url = "/admin/certification/definitions",
    action = {
      @AuthorizationAction(
          actionUri = PageAdminCertification.AUTH_CERTIFICATION_ALL,
          label = PageAdminCertification.AUTH_CERTIFICATION_ALL_LABEL,
          description = PageAdminCertification.AUTH_CERTIFICATION_ALL_DESCRIPTION),
      @AuthorizationAction(
          actionUri = PageAdminCertification.AUTH_CERTIFICATION_DEFINITIONS,
          label = PageAdminCertification.AUTH_CERTIFICATION_DEFINITIONS_LABEL,
          description = PageAdminCertification.AUTH_CERTIFICATION_DEFINITIONS_DESCRIPTION)
    })
public class PageCertDefinitions extends PageAdminWorkItems {

  private static final long serialVersionUID = 1L;

  private static final String ID_MAIN_FORM = "mainForm";
  private static final String ID_TABLE = "table";

  private static final String DOT_CLASS = PageCertDefinitions.class.getName() + ".";
  private static final String OPERATION_CREATE_CAMPAIGN = DOT_CLASS + "createCampaign";
  private static final String OPERATION_DELETE_DEFINITION = DOT_CLASS + "deleteDefinition";

  private static final Trace LOGGER = TraceManager.getTrace(PageCertDefinitions.class);

  private AccessCertificationDefinitionType singleDelete;

  public PageCertDefinitions() {
    initLayout();
  }

  private void initLayout() {
    Form mainForm = new Form(ID_MAIN_FORM);
    add(mainForm);

    MainObjectListPanel<AccessCertificationDefinitionType> mainPanel =
        new MainObjectListPanel<AccessCertificationDefinitionType>(
            ID_TABLE,
            AccessCertificationDefinitionType.class,
            TableId.PAGE_CERT_DEFINITIONS_PANEL,
            null,
            this) {
          private static final long serialVersionUID = 1L;

          @Override
          protected IColumn<SelectableBean<AccessCertificationDefinitionType>, String>
              createCheckboxColumn() {
            return null;
          }

          @Override
          public void objectDetailsPerformed(
              AjaxRequestTarget target, AccessCertificationDefinitionType service) {
            PageCertDefinitions.this.detailsPerformed(target, service);
          }

          @Override
          protected List<IColumn<SelectableBean<AccessCertificationDefinitionType>, String>>
              createColumns() {
            return PageCertDefinitions.this.initColumns();
          }

          @Override
          protected List<InlineMenuItem> createInlineMenu() {
            return null;
          }

          @Override
          protected void newObjectPerformed(AjaxRequestTarget target) {
            navigateToNext(PageCertDefinition.class);
          }
        };
    mainPanel.setAdditionalBoxCssClasses(GuiStyleConstants.CLASS_OBJECT_CERT_DEF_BOX_CSS_CLASSES);
    mainForm.add(mainPanel);
  }

  private MainObjectListPanel<AccessCertificationDefinitionType> getDefinitionsTable() {
    return (MainObjectListPanel<AccessCertificationDefinitionType>)
        get(createComponentPath(ID_MAIN_FORM, ID_TABLE));
  }

  private IModel<String> createDeleteConfirmString() {
    return new AbstractReadOnlyModel<String>() {
      @Override
      public String getObject() {
        if (singleDelete == null) {
          return "";
        } else {
          return createStringResource(
                  "PageCertDefinitions.deleteDefinitionConfirmSingle", singleDelete.getName())
              .getString();
        }
      }
    };
  }

  private List<IColumn<SelectableBean<AccessCertificationDefinitionType>, String>> initColumns() {
    List<IColumn<SelectableBean<AccessCertificationDefinitionType>, String>> columns =
        new ArrayList<>();

    IColumn column;

    column =
        new PropertyColumn(
            createStringResource("PageCertDefinitions.table.description"), "value.description");
    columns.add(column);

    column =
        new MultiButtonColumn<SelectableBean<AccessCertificationDefinitionType>>(new Model(), 3) {

          private final String[] captionKeys = {
            "PageCertDefinitions.button.createCampaign",
            "PageCertDefinitions.button.showCampaigns",
            "PageCertDefinitions.button.deleteDefinition"
          };

          private final DoubleButtonColumn.BUTTON_COLOR_CLASS[] colors = {
            DoubleButtonColumn.BUTTON_COLOR_CLASS.PRIMARY,
            DoubleButtonColumn.BUTTON_COLOR_CLASS.DEFAULT,
            DoubleButtonColumn.BUTTON_COLOR_CLASS.DANGER
          };

          @Override
          public String getCaption(int id) {
            return PageCertDefinitions.this.createStringResource(captionKeys[id]).getString();
          }

          @Override
          public String getButtonColorCssClass(int id) {
            return colors[id].toString();
          }

          @Override
          public void clickPerformed(
              int id,
              AjaxRequestTarget target,
              IModel<SelectableBean<AccessCertificationDefinitionType>> model) {
            switch (id) {
              case 0:
                createCampaignPerformed(target, model.getObject().getValue());
                break;
              case 1:
                showCampaignsPerformed(target, model.getObject().getValue());
                break;
              case 2:
                deleteConfirmation(target, model.getObject().getValue());
                break;
            }
          }
        };
    columns.add(column);

    return columns;
  }

  protected void detailsPerformed(
      AjaxRequestTarget target, AccessCertificationDefinitionType service) {
    PageParameters parameters = new PageParameters();
    parameters.add(OnePageParameterEncoder.PARAMETER, service.getOid());
    navigateToNext(PageCertDefinition.class, parameters);
  }

  private void showCampaignsPerformed(
      AjaxRequestTarget target, AccessCertificationDefinitionType definition) {
    PageParameters parameters = new PageParameters();
    parameters.add(OnePageParameterEncoder.PARAMETER, definition.getOid());
    navigateToNext(PageCertCampaigns.class, parameters);
  }

  private void createCampaignPerformed(
      AjaxRequestTarget target, AccessCertificationDefinitionType definition) {
    LOGGER.debug("Create certification campaign performed for {}", definition.asPrismObject());

    OperationResult result = new OperationResult(OPERATION_CREATE_CAMPAIGN);
    try {
      Task task = createSimpleTask(OPERATION_CREATE_CAMPAIGN);
      getCertificationService().createCampaign(definition.getOid(), task, result);
    } catch (Exception ex) {
      result.recordFatalError(ex);
    } finally {
      result.computeStatusIfUnknown();
    }

    showResult(result);
    target.add(getFeedbackPanel());
  }

  private void deleteConfirmation(
      AjaxRequestTarget target, AccessCertificationDefinitionType definition) {

    this.singleDelete = definition;
    showMainPopup(getDeleteDefinitionConfirmationPanel(), target);
  }

  private void deleteDefinitionPerformed(
      AjaxRequestTarget target, AccessCertificationDefinitionType definition) {
    OperationResult result = new OperationResult(OPERATION_DELETE_DEFINITION);
    try {
      Task task = createSimpleTask(OPERATION_DELETE_DEFINITION);
      ObjectDelta<AccessCertificationDefinitionType> delta =
          ObjectDelta.createDeleteDelta(
              AccessCertificationDefinitionType.class, definition.getOid(), getPrismContext());
      getModelService()
          .executeChanges(WebComponentUtil.createDeltaCollection(delta), null, task, result);
    } catch (Exception ex) {
      result.recordPartialError("Couldn't delete campaign definition.", ex);
      LoggingUtils.logUnexpectedException(LOGGER, "Couldn't delete campaign definition", ex);
    }

    result.computeStatusIfUnknown();
    if (result.isSuccess()) {
      result.recordStatus(
          OperationResultStatus.SUCCESS, "The definition has been successfully deleted.");
    }

    getDefinitionsTable().clearCache();

    showResult(result);
    target.add(getFeedbackPanel(), getDefinitionsTable());
  }

  private Popupable getDeleteDefinitionConfirmationPanel() {
    return new ConfirmationPanel(getMainPopupBodyId(), createDeleteConfirmString()) {
      @Override
      public void yesPerformed(AjaxRequestTarget target) {
        ModalWindow modalWindow = findParent(ModalWindow.class);
        if (modalWindow != null) {
          modalWindow.close(target);
          deleteDefinitionPerformed(target, singleDelete);
        }
      }
    };
  }
}
Example #22
0
/**
 * Nested Operation Result.
 *
 * <p>This class provides information for better error handling in complex operations. It contains a
 * status (success, failure, warning, ...) and an error message. It also contains a set of
 * sub-results - results on inner operations.
 *
 * <p>This object can be used by GUI to display smart (and interactive) error information. It can
 * also be used by the client code to detect deeper problems in the invocations, retry or otherwise
 * compensate for the errors or decide how severe the error was and it is possible to proceed.
 *
 * @author lazyman
 * @author Radovan Semancik
 */
public class OperationResult implements Serializable, DebugDumpable, Cloneable {

  private static final long serialVersionUID = -2467406395542291044L;
  private static final String INDENT_STRING = "    ";

  /**
   * This constant provides count threshold for same subresults (same operation and status) during
   * summarize operation.
   */
  private static final int SUBRESULT_STRIP_THRESHOLD = 10;

  public static final String CONTEXT_IMPLEMENTATION_CLASS = "implementationClass";
  public static final String CONTEXT_PROGRESS = "progress";
  public static final String CONTEXT_OID = "oid";
  public static final String CONTEXT_OBJECT = "object";
  public static final String CONTEXT_ITEM = "item";
  public static final String CONTEXT_TASK = "task";
  public static final String CONTEXT_RESOURCE = "resource";

  public static final String PARAM_OID = "oid";
  public static final String PARAM_TYPE = "type";
  public static final String PARAM_OPTIONS = "options";
  public static final String PARAM_TASK = "task";
  public static final String PARAM_OBJECT = "object";
  public static final String PARAM_QUERY = "query";

  public static final String RETURN_COUNT = "count";
  public static final String RETURN_BACKGROUND_TASK_OID = "backgroundTaskOid";

  private static long TOKEN_COUNT = 1000000000000000000L;
  private String operation;
  private OperationResultStatus status;
  private Map<String, Serializable> params;
  private Map<String, Serializable> context;
  private Map<String, Serializable> returns;
  private long token;
  private String messageCode;
  private String message;
  private String localizationMessage;
  private List<Serializable> localizationArguments;
  private Throwable cause;
  private int count = 1;
  private List<OperationResult> subresults;
  private List<String> details;
  private boolean summarizeErrors;
  private boolean summarizePartialErrors;
  private boolean summarizeSuccesses;
  private boolean minor = false;

  private static final Trace LOGGER = TraceManager.getTrace(OperationResult.class);

  public OperationResult(String operation) {
    this(operation, null, OperationResultStatus.UNKNOWN, 0, null, null, null, null, null);
  }

  public OperationResult(String operation, String messageCode, String message) {
    this(operation, null, OperationResultStatus.SUCCESS, 0, messageCode, message, null, null, null);
  }

  public OperationResult(String operation, long token, String messageCode, String message) {
    this(
        operation,
        null,
        OperationResultStatus.SUCCESS,
        token,
        messageCode,
        message,
        null,
        null,
        null);
  }

  public OperationResult(String operation, OperationResultStatus status, String message) {
    this(operation, null, status, 0, null, message, null, null, null);
  }

  public OperationResult(
      String operation, OperationResultStatus status, String messageCode, String message) {
    this(operation, null, status, 0, messageCode, message, null, null, null);
  }

  public OperationResult(
      String operation,
      OperationResultStatus status,
      long token,
      String messageCode,
      String message) {
    this(operation, null, status, token, messageCode, message, null, null, null);
  }

  public OperationResult(
      String operation,
      OperationResultStatus status,
      long token,
      String messageCode,
      String message,
      Throwable cause) {
    this(operation, null, status, token, messageCode, message, null, cause, null);
  }

  public OperationResult(
      String operation,
      Map<String, Serializable> params,
      OperationResultStatus status,
      long token,
      String messageCode,
      String message) {
    this(operation, params, status, token, messageCode, message, null, null, null);
  }

  public OperationResult(
      String operation,
      Map<String, Serializable> params,
      OperationResultStatus status,
      long token,
      String messageCode,
      String message,
      List<OperationResult> subresults) {
    this(operation, params, status, token, messageCode, message, null, null, subresults);
  }

  public OperationResult(
      String operation,
      Map<String, Serializable> params,
      OperationResultStatus status,
      long token,
      String messageCode,
      String message,
      String localizationMessage,
      Throwable cause,
      List<OperationResult> subresults) {
    this(
        operation,
        params,
        status,
        token,
        messageCode,
        message,
        localizationMessage,
        null,
        cause,
        subresults);
  }

  public OperationResult(
      String operation,
      Map<String, Serializable> params,
      OperationResultStatus status,
      long token,
      String messageCode,
      String message,
      String localizationMessage,
      List<Serializable> localizationArguments,
      Throwable cause,
      List<OperationResult> subresults) {
    this(
        operation,
        params,
        null,
        null,
        status,
        token,
        messageCode,
        message,
        localizationMessage,
        null,
        cause,
        subresults);
  }

  public OperationResult(
      String operation,
      Map<String, Serializable> params,
      Map<String, Serializable> context,
      Map<String, Serializable> returns,
      OperationResultStatus status,
      long token,
      String messageCode,
      String message,
      String localizationMessage,
      List<Serializable> localizationArguments,
      Throwable cause,
      List<OperationResult> subresults) {
    if (StringUtils.isEmpty(operation)) {
      throw new IllegalArgumentException("Operation argument must not be null or empty.");
    }
    if (status == null) {
      throw new IllegalArgumentException("Operation status must not be null.");
    }
    this.operation = operation;
    this.params = params;
    this.context = context;
    this.returns = returns;
    this.status = status;
    this.token = token;
    this.messageCode = messageCode;
    this.message = message;
    this.localizationMessage = localizationMessage;
    this.localizationArguments = localizationArguments;
    this.cause = cause;
    this.subresults = subresults;
    this.details = new ArrayList<String>();
  }

  public OperationResult createSubresult(String operation) {
    OperationResult subresult = new OperationResult(operation);
    addSubresult(subresult);
    return subresult;
  }

  public OperationResult createMinorSubresult(String operation) {
    OperationResult subresult = createSubresult(operation);
    subresult.minor = true;
    return subresult;
  }

  /**
   * Contains operation name. Operation name must be defined as {@link String} constant in module
   * interface with description and possible parameters. It can be used for further processing. It
   * will be used as key for translation in admin-gui.
   *
   * @return always return non null, non empty string
   */
  public String getOperation() {
    return operation;
  }

  public int getCount() {
    return count;
  }

  public void setCount(int count) {
    this.count = count;
  }

  public void incrementCount() {
    this.count++;
  }

  public boolean isSummarizeErrors() {
    return summarizeErrors;
  }

  public void setSummarizeErrors(boolean summarizeErrors) {
    this.summarizeErrors = summarizeErrors;
  }

  public boolean isSummarizePartialErrors() {
    return summarizePartialErrors;
  }

  public void setSummarizePartialErrors(boolean summarizePartialErrors) {
    this.summarizePartialErrors = summarizePartialErrors;
  }

  public boolean isSummarizeSuccesses() {
    return summarizeSuccesses;
  }

  public void setSummarizeSuccesses(boolean summarizeSuccesses) {
    this.summarizeSuccesses = summarizeSuccesses;
  }

  public boolean isEmpty() {
    return (status == null || status == OperationResultStatus.UNKNOWN)
        && (subresults == null || subresults.isEmpty());
  }

  /**
   * Method returns list of operation subresults @{link {@link OperationResult}.
   *
   * @return never returns null
   */
  public List<OperationResult> getSubresults() {
    if (subresults == null) {
      subresults = new ArrayList<OperationResult>();
    }
    return subresults;
  }

  /** @return last subresult, or null if there are no subresults. */
  public OperationResult getLastSubresult() {
    if (subresults == null || subresults.isEmpty()) {
      return null;
    } else {
      return subresults.get(subresults.size() - 1);
    }
  }

  public void removeLastSubresult() {
    if (subresults != null && !subresults.isEmpty()) {
      subresults.remove(subresults.size() - 1);
    }
  }

  /** @return last subresult status, or null if there are no subresults. */
  public OperationResultStatus getLastSubresultStatus() {
    OperationResult last = getLastSubresult();
    return last != null ? last.getStatus() : null;
  }

  public void addSubresult(OperationResult subresult) {
    getSubresults().add(subresult);
  }

  public OperationResult findSubresult(String operation) {
    if (subresults == null) {
      return null;
    }
    for (OperationResult subResult : getSubresults()) {
      if (operation.equals(subResult.getOperation())) {
        return subResult;
      }
    }
    return null;
  }

  public List<OperationResult> findSubresults(String operation) {
    List<OperationResult> found = new ArrayList<>();
    if (subresults == null) {
      return found;
    }
    for (OperationResult subResult : getSubresults()) {
      if (operation.equals(subResult.getOperation())) {
        found.add(subResult);
      }
    }
    return found;
  }

  /**
   * Contains operation status as defined in {@link OperationResultStatus}
   *
   * @return never returns null
   */
  public OperationResultStatus getStatus() {
    return status;
  }

  public void setStatus(OperationResultStatus status) {
    this.status = status;
  }

  /**
   * Returns true if the result is success.
   *
   * <p>This returns true if the result is absolute success. Presence of partial failures or
   * warnings fail this test.
   *
   * @return true if the result is success.
   */
  public boolean isSuccess() {
    return (status == OperationResultStatus.SUCCESS);
  }

  public boolean isWarning() {
    return status == OperationResultStatus.WARNING;
  }

  /**
   * Returns true if the result is acceptable for further processing.
   *
   * <p>In other words: if there were no fatal errors. Warnings and partial errors are acceptable.
   * Yet, this test also fails if the operation state is not known.
   *
   * @return true if the result is acceptable for further processing.
   */
  public boolean isAcceptable() {
    return (status != OperationResultStatus.FATAL_ERROR);
  }

  public boolean isUnknown() {
    return (status == OperationResultStatus.UNKNOWN);
  }

  public boolean isInProgress() {
    return (status == OperationResultStatus.IN_PROGRESS);
  }

  public boolean isError() {
    return (status == OperationResultStatus.FATAL_ERROR)
        || (status == OperationResultStatus.PARTIAL_ERROR);
  }

  public boolean isFatalError() {
    return (status == OperationResultStatus.FATAL_ERROR);
  }

  public boolean isPartialError() {
    return (status == OperationResultStatus.PARTIAL_ERROR);
  }

  public boolean isHandledError() {
    return (status == OperationResultStatus.HANDLED_ERROR);
  }

  public boolean isNotApplicable() {
    return (status == OperationResultStatus.NOT_APPLICABLE);
  }

  /** Set all error status in this result and all subresults as handled. */
  public void setErrorsHandled() {
    if (isError()) {
      setStatus(OperationResultStatus.HANDLED_ERROR);
    }
    for (OperationResult subresult : getSubresults()) {
      subresult.setErrorsHandled();
    }
  }

  /**
   * Computes operation result status based on subtask status and sets an error message if the
   * status is FATAL_ERROR.
   *
   * @param errorMessage error message
   */
  public void computeStatus(String errorMessage) {
    computeStatus(errorMessage, errorMessage);
  }

  public void computeStatus(String errorMessage, String warnMessage) {
    Validate.notEmpty(errorMessage, "Error message must not be null.");

    // computeStatus sets a message if none is set,
    // therefore we need to check before calling computeStatus
    boolean noMessage = StringUtils.isEmpty(message);
    computeStatus();

    switch (status) {
      case FATAL_ERROR:
      case PARTIAL_ERROR:
        if (noMessage) {
          message = errorMessage;
        }
        break;
      case UNKNOWN:
      case WARNING:
      case NOT_APPLICABLE:
        if (noMessage) {
          if (StringUtils.isNotEmpty(warnMessage)) {
            message = warnMessage;
          } else {
            message = errorMessage;
          }
        }
        break;
    }
  }

  /** Computes operation result status based on subtask status. */
  public void computeStatus() {
    if (getSubresults().isEmpty()) {
      if (status == OperationResultStatus.UNKNOWN) {
        status = OperationResultStatus.SUCCESS;
      }
      return;
    }
    if (status == OperationResultStatus.FATAL_ERROR) {
      return;
    }
    OperationResultStatus newStatus = OperationResultStatus.UNKNOWN;
    boolean allSuccess = true;
    boolean allNotApplicable = true;
    String newMessage = null;
    for (OperationResult sub : getSubresults()) {
      if (sub.getStatus() != OperationResultStatus.NOT_APPLICABLE) {
        allNotApplicable = false;
      }
      if (sub.getStatus() == OperationResultStatus.FATAL_ERROR) {
        status = OperationResultStatus.FATAL_ERROR;
        if (message == null) {
          message = sub.getMessage();
        } else {
          message = message + ": " + sub.getMessage();
        }
        return;
      }
      if (sub.getStatus() == OperationResultStatus.IN_PROGRESS) {
        status = OperationResultStatus.IN_PROGRESS;
        if (message == null) {
          message = sub.getMessage();
        } else {
          message = message + ": " + sub.getMessage();
        }
        return;
      }
      if (sub.getStatus() == OperationResultStatus.PARTIAL_ERROR) {
        newStatus = OperationResultStatus.PARTIAL_ERROR;
        newMessage = sub.getMessage();
      }
      if (newStatus != OperationResultStatus.PARTIAL_ERROR) {
        if (sub.getStatus() == OperationResultStatus.HANDLED_ERROR) {
          newStatus = OperationResultStatus.HANDLED_ERROR;
          newMessage = sub.getMessage();
        }
      }
      if (sub.getStatus() != OperationResultStatus.SUCCESS
          && sub.getStatus() != OperationResultStatus.NOT_APPLICABLE) {
        allSuccess = false;
      }
      if (newStatus != OperationResultStatus.HANDLED_ERROR) {
        if (sub.getStatus() == OperationResultStatus.WARNING) {
          newStatus = OperationResultStatus.WARNING;
          newMessage = sub.getMessage();
        }
      }
    }

    if (allNotApplicable && !getSubresults().isEmpty()) {
      status = OperationResultStatus.NOT_APPLICABLE;
    }
    if (allSuccess && !getSubresults().isEmpty()) {
      status = OperationResultStatus.SUCCESS;
    } else {
      status = newStatus;
      if (message == null) {
        message = newMessage;
      } else {
        message = message + ": " + newMessage;
      }
    }
  }

  /**
   * Used when the result contains several composite sub-result that are of equivalent meaning. If
   * all of them fail the result will be fatal error as well. If only some of them fail the result
   * will be partial error. Handled error is considered a success.
   */
  public void computeStatusComposite() {
    if (getSubresults().isEmpty()) {
      if (status == OperationResultStatus.UNKNOWN) {
        status = OperationResultStatus.NOT_APPLICABLE;
      }
      return;
    }

    boolean allFatalError = true;
    boolean allNotApplicable = true;
    boolean hasInProgress = false;
    boolean hasHandledError = false;
    boolean hasError = false;
    boolean hasWarning = false;
    for (OperationResult sub : getSubresults()) {
      if (sub.getStatus() != OperationResultStatus.NOT_APPLICABLE) {
        allNotApplicable = false;
      }
      if (sub.getStatus() != OperationResultStatus.FATAL_ERROR) {
        allFatalError = false;
      }
      if (sub.getStatus() == OperationResultStatus.FATAL_ERROR) {
        hasError = true;
        if (message == null) {
          message = sub.getMessage();
        } else {
          message = message + ", " + sub.getMessage();
        }
      }
      if (sub.getStatus() == OperationResultStatus.PARTIAL_ERROR) {
        hasError = true;
        if (message == null) {
          message = sub.getMessage();
        } else {
          message = message + ", " + sub.getMessage();
        }
      }
      if (sub.getStatus() == OperationResultStatus.HANDLED_ERROR) {
        hasHandledError = true;
        if (message == null) {
          message = sub.getMessage();
        } else {
          message = message + ", " + sub.getMessage();
        }
      }
      if (sub.getStatus() == OperationResultStatus.IN_PROGRESS) {
        hasInProgress = true;
        if (message == null) {
          message = sub.getMessage();
        } else {
          message = message + ", " + sub.getMessage();
        }
      }
      if (sub.getStatus() == OperationResultStatus.WARNING) {
        hasWarning = true;
        if (message == null) {
          message = sub.getMessage();
        } else {
          message = message + ", " + sub.getMessage();
        }
      }
    }

    if (allNotApplicable) {
      status = OperationResultStatus.NOT_APPLICABLE;
    } else if (allFatalError) {
      status = OperationResultStatus.FATAL_ERROR;
    } else if (hasInProgress) {
      status = OperationResultStatus.IN_PROGRESS;
    } else if (hasError) {
      status = OperationResultStatus.PARTIAL_ERROR;
    } else if (hasWarning) {
      status = OperationResultStatus.WARNING;
    } else if (hasHandledError) {
      status = OperationResultStatus.HANDLED_ERROR;
    } else {
      status = OperationResultStatus.SUCCESS;
    }
  }

  public OperationResultStatus getComputeStatus() {
    OperationResultStatus origStatus = status;
    String origMessage = message;
    computeStatus();
    OperationResultStatus computedStatus = status;
    status = origStatus;
    message = origMessage;
    return computedStatus;
  }

  public void computeStatusIfUnknown() {
    if (isUnknown()) {
      computeStatus();
    }
  }

  public void recomputeStatus() {
    // Only recompute if there are subresults, otherwise keep original
    // status
    if (subresults != null && !subresults.isEmpty()) {
      computeStatus();
    }
  }

  public void recomputeStatus(String message) {
    // Only recompute if there are subresults, otherwise keep original
    // status
    if (subresults != null && !subresults.isEmpty()) {
      computeStatus(message);
    }
  }

  public void recomputeStatus(String errorMessage, String warningMessage) {
    // Only recompute if there are subresults, otherwise keep original
    // status
    if (subresults != null && !subresults.isEmpty()) {
      computeStatus(errorMessage, warningMessage);
    }
  }

  public void recordSuccessIfUnknown() {
    if (isUnknown()) {
      recordSuccess();
    }
  }

  public void recordNotApplicableIfUnknown() {
    if (isUnknown()) {
      status = OperationResultStatus.NOT_APPLICABLE;
    }
  }

  /**
   * Method returns {@link Map} with operation parameters. Parameters keys are described in module
   * interface for every operation.
   *
   * @return never returns null
   */
  public Map<String, Serializable> getParams() {
    if (params == null) {
      params = new HashMap<String, Serializable>();
    }
    return params;
  }

  public void addParam(String paramName, Serializable paramValue) {
    getParams().put(paramName, paramValue);
  }

  public void addArbitraryObjectAsParam(String paramName, Object paramValue) {
    addParam(paramName, String.valueOf(paramValue));
  }

  // Copies a collection to a OperationResult's param field. Primarily used to overcome the fact
  // that Collection is not Serializable
  public void addCollectionOfSerializablesAsParam(
      String paramName, Collection<? extends Serializable> paramValue) {
    addParam(paramName, paramValue != null ? new ArrayList(paramValue) : null);
  }

  public void addCollectionOfSerializablesAsReturn(
      String name, Collection<? extends Serializable> value) {
    addReturn(name, value != null ? new ArrayList(value) : null);
  }

  public void addArbitraryCollectionAsParam(String paramName, Collection values) {
    if (values != null) {
      ArrayList<String> valuesAsStrings = new ArrayList<String>();
      for (Object value : values) {
        valuesAsStrings.add(String.valueOf(value));
      }
      addParam(paramName, valuesAsStrings);
    } else {
      addParam(paramName, null);
    }
  }

  public void addParams(String[] names, Serializable... objects) {
    if (names.length != objects.length) {
      throw new IllegalArgumentException(
          "Bad result parameters size, names '"
              + names.length
              + "', objects '"
              + objects.length
              + "'.");
    }

    for (int i = 0; i < names.length; i++) {
      addParam(names[i], objects[i]);
    }
  }

  public Map<String, Serializable> getContext() {
    if (context == null) {
      context = new HashMap<String, Serializable>();
    }
    return context;
  }

  @SuppressWarnings("unchecked")
  public <T> T getContext(Class<T> type, String contextName) {
    return (T) getContext().get(contextName);
  }

  public void addContext(String contextName, Serializable value) {
    getContext().put(contextName, value);
  }

  public Map<String, Serializable> getReturns() {
    if (returns == null) {
      returns = new HashMap<>();
    }
    return returns;
  }

  public void addReturn(String returnName, Serializable value) {
    getReturns().put(returnName, value);
  }

  public Serializable getReturn(String returnName) {
    return getReturns().get(returnName);
  }

  /** @return Contains random long number, for better searching in logs. */
  public long getToken() {
    if (token == 0) {
      token = TOKEN_COUNT++;
    }
    return token;
  }

  /**
   * Contains mesage code based on module error catalog.
   *
   * @return Can return null.
   */
  public String getMessageCode() {
    return messageCode;
  }

  /**
   * @return Method returns operation result message. Message is required. It will be key for
   *     translation in admin-gui.
   */
  public String getMessage() {
    return message;
  }

  public void setMessage(String message) {
    this.message = message;
  }

  /** @return Method returns message key for translation, can be null. */
  public String getLocalizationMessage() {
    return localizationMessage;
  }

  /** @return Method returns arguments if needed for localization, can be null. */
  public List<Serializable> getLocalizationArguments() {
    return localizationArguments;
  }

  /** @return Method returns operation result exception. Not required, can be null. */
  public Throwable getCause() {
    return cause;
  }

  public void recordSuccess() {
    // Success, no message or other explanation is needed.
    status = OperationResultStatus.SUCCESS;
  }

  public void recordInProgress() {
    status = OperationResultStatus.IN_PROGRESS;
  }

  public void recordUnknown() {
    status = OperationResultStatus.UNKNOWN;
  }

  public void recordFatalError(Throwable cause) {
    recordStatus(OperationResultStatus.FATAL_ERROR, cause.getMessage(), cause);
  }

  /**
   * If the operation is an error then it will switch the status to EXPECTED_ERROR. This is used if
   * the error is expected and properly handled.
   */
  public void muteError() {
    if (isError()) {
      status = OperationResultStatus.HANDLED_ERROR;
    }
  }

  public void muteLastSubresultError() {
    OperationResult lastSubresult = getLastSubresult();
    if (lastSubresult != null) {
      lastSubresult.muteError();
    }
  }

  public void recordPartialError(Throwable cause) {
    recordStatus(OperationResultStatus.PARTIAL_ERROR, cause.getMessage(), cause);
  }

  public void recordWarning(Throwable cause) {
    recordStatus(OperationResultStatus.WARNING, cause.getMessage(), cause);
  }

  public void recordStatus(OperationResultStatus status, Throwable cause) {
    this.status = status;
    this.cause = cause;
    // No other message was given, so use message from the exception
    // not really correct, but better than nothing.
    message = cause.getMessage();
  }

  public void recordFatalError(String message, Throwable cause) {
    recordStatus(OperationResultStatus.FATAL_ERROR, message, cause);
  }

  public void recordPartialError(String message, Throwable cause) {
    recordStatus(OperationResultStatus.PARTIAL_ERROR, message, cause);
  }

  public void recordWarning(String message, Throwable cause) {
    recordStatus(OperationResultStatus.WARNING, message, cause);
  }

  public void recordHandledError(String message) {
    recordStatus(OperationResultStatus.HANDLED_ERROR, message);
  }

  public void recordHandledError(String message, Throwable cause) {
    recordStatus(OperationResultStatus.HANDLED_ERROR, message, cause);
  }

  public void recordHandledError(Throwable cause) {
    recordStatus(OperationResultStatus.HANDLED_ERROR, cause.getMessage(), cause);
  }

  public void recordStatus(OperationResultStatus status, String message, Throwable cause) {
    this.status = status;
    this.message = message;
    this.cause = cause;
  }

  public void recordFatalError(String message) {
    recordStatus(OperationResultStatus.FATAL_ERROR, message);
  }

  public void recordPartialError(String message) {
    recordStatus(OperationResultStatus.PARTIAL_ERROR, message);
  }

  public void recordWarning(String message) {
    recordStatus(OperationResultStatus.WARNING, message);
  }

  /**
   * Records result from a common exception type. This automatically determines status and also sets
   * appropriate message.
   *
   * @param exception common exception
   */
  public void record(CommonException exception) {
    // TODO: switch to a localized message later
    // Exception is a fatal error in this context
    recordFatalError(exception.getOperationResultMessage(), exception);
  }

  public void recordStatus(OperationResultStatus status, String message) {
    this.status = status;
    this.message = message;
  }

  /**
   * Returns true if result status is UNKNOWN or any of the subresult status is unknown (recursive).
   *
   * <p>May come handy in tests to check if all the operations fill out the status as they should.
   */
  public boolean hasUnknownStatus() {
    if (status == OperationResultStatus.UNKNOWN) {
      return true;
    }
    for (OperationResult subresult : getSubresults()) {
      if (subresult.hasUnknownStatus()) {
        return true;
      }
    }
    return false;
  }

  public void appendDetail(String detailLine) {
    // May be switched to a more structured method later
    details.add(detailLine);
  }

  public List<String> getDetail() {
    return details;
  }

  @Override
  public String toString() {
    return "R(" + operation + " " + status + " " + message + ")";
  }

  public static OperationResult createOperationResult(OperationResultType result) {
    if (result == null) {
      return null;
    }

    Map<String, Serializable> params = ParamsTypeUtil.fromParamsType(result.getParams());
    //		if (result.getParams() != null) {
    //			params = new HashMap<String, Serializable>();
    //			for (EntryType entry : result.getParams().getEntry()) {
    //				params.put(entry.getKey(), (Serializable) entry.getEntryValue());
    //			}
    //		}

    Map<String, Serializable> context = ParamsTypeUtil.fromParamsType(result.getContext());
    //		if (result.getContext() != null) {
    //			context = new HashMap<String, Serializable>();
    //			for (EntryType entry : result.getContext().getEntry()) {
    //				context.put(entry.getKey(), (Serializable) entry.getEntryValue());
    //			}
    //		}

    Map<String, Serializable> returns = ParamsTypeUtil.fromParamsType(result.getReturns());
    //		if (result.getReturns() != null) {
    //			returns = new HashMap<String, Serializable>();
    //			for (EntryType entry : result.getReturns().getEntry()) {
    //				returns.put(entry.getKey(), (Serializable) entry.getEntryValue());
    //			}
    //		}

    List<OperationResult> subresults = null;
    if (!result.getPartialResults().isEmpty()) {
      subresults = new ArrayList<OperationResult>();
      for (OperationResultType subResult : result.getPartialResults()) {
        subresults.add(createOperationResult(subResult));
      }
    }

    LocalizedMessageType message = result.getLocalizedMessage();
    String localizedMessage = message == null ? null : message.getKey();
    List<Serializable> localizedArguments =
        message == null
            ? null
            : (List<Serializable>) (List) message.getArgument(); // FIXME: brutal hack

    OperationResult opResult =
        new OperationResult(
            result.getOperation(),
            params,
            context,
            returns,
            OperationResultStatus.parseStatusType(result.getStatus()),
            result.getToken(),
            result.getMessageCode(),
            result.getMessage(),
            localizedMessage,
            localizedArguments,
            null,
            subresults);
    if (result.getCount() != null) {
      opResult.setCount(result.getCount());
    }
    return opResult;
  }

  public OperationResultType createOperationResultType() {
    return createOperationResultType(this);
  }

  private OperationResultType createOperationResultType(OperationResult opResult) {
    OperationResultType result = new OperationResultType();
    result.setToken(opResult.getToken());
    result.setStatus(OperationResultStatus.createStatusType(opResult.getStatus()));
    if (opResult.getCount() != 1) {
      result.setCount(opResult.getCount());
    }
    result.setOperation(opResult.getOperation());
    result.setMessage(opResult.getMessage());
    result.setMessageCode(opResult.getMessageCode());

    if (opResult.getCause() != null || !opResult.details.isEmpty()) {
      StringBuilder detailsb = new StringBuilder();

      // Record text messages in details (if present)
      if (!opResult.details.isEmpty()) {
        for (String line : opResult.details) {
          detailsb.append(line);
          detailsb.append("\n");
        }
      }

      // Record stack trace in details if a cause is present
      if (opResult.getCause() != null) {
        Throwable ex = opResult.getCause();
        detailsb.append(ex.getClass().getName());
        detailsb.append(": ");
        detailsb.append(ex.getMessage());
        detailsb.append("\n");
        StackTraceElement[] stackTrace = ex.getStackTrace();
        for (int i = 0; i < stackTrace.length; i++) {
          detailsb.append(stackTrace[i].toString());
          detailsb.append("\n");
        }
      }

      result.setDetails(detailsb.toString());
    }

    if (StringUtils.isNotEmpty(opResult.getLocalizationMessage())) {
      LocalizedMessageType message = new LocalizedMessageType();
      message.setKey(opResult.getLocalizationMessage());
      if (opResult.getLocalizationArguments() != null) {
        message.getArgument().addAll(opResult.getLocalizationArguments());
      }
      result.setLocalizedMessage(message);
    }

    //		Set<Entry<String, Serializable>> params = opResult.getParams();
    //		if (!params.isEmpty()) {
    ParamsType paramsType = ParamsTypeUtil.toParamsType(opResult.getParams());
    result.setParams(paramsType);

    //			for (Entry<String, Serializable> entry : params) {
    //				paramsType.getEntry().add(createEntryElement(entry.getKey(),entry.getValue()));
    //			}
    //		}

    //		Set<Entry<String, Serializable>> context = opResult.getContext().entrySet();
    //		if (!context.isEmpty()) {
    paramsType = ParamsTypeUtil.toParamsType(opResult.getContext());
    result.setContext(paramsType);

    //			for (Entry<String, Serializable> entry : context) {
    //				paramsType.getEntry().add(createEntryElement(entry.getKey(),entry.getValue()));
    //			}
    //		}

    //		Set<Entry<String, Serializable>> returns = opResult.getReturns().entrySet();
    //		if (!returns.isEmpty()) {
    paramsType = ParamsTypeUtil.toParamsType(opResult.getReturns());
    result.setReturns(paramsType);

    //			for (Entry<String, Serializable> entry : returns) {
    //				paramsType.getEntry().add(createEntryElement(entry.getKey(),entry.getValue()));
    //			}
    //		}

    for (OperationResult subResult : opResult.getSubresults()) {
      result.getPartialResults().add(opResult.createOperationResultType(subResult));
    }

    return result;
  }

  //	/**
  //	 * Temporary workaround, brutally hacked -- so that the conversion
  //	 * of OperationResult into OperationResultType 'somehow' works, at least to the point
  //	 * where when we:
  //	 * - have OR1
  //	 * - serialize it into ORT1
  //	 * - then deserialize into OR2
  //	 * - serialize again into ORT2
  //	 * so we get ORT1.equals(ORT2) - at least in our simple test case :)
  //	 *
  //	 * FIXME: this should be definitely reworked
  //	 *
  //	 * @param entry
  //	 * @return
  //	 */
  //	private EntryType createEntryElement(String key, Serializable value) {
  //		EntryType entryType = new EntryType();
  //		entryType.setKey(key);
  //		if (value != null) {
  //			Document doc = DOMUtil.getDocument();
  //			if (value instanceof ObjectType && ((ObjectType)value).getOid() != null) {
  //				// Store only reference on the OID. This is faster and getObject can be used to retrieve
  //				// the object if needed. Although is does not provide 100% accuracy, it is a good tradeoff.
  //				setObjectReferenceEntry(entryType, ((ObjectType)value));
  //			// these values should be put 'as they are', in order to be deserialized into themselves
  //			} else if (value instanceof String || value instanceof Integer || value instanceof Long) {
  //				entryType.setEntryValue(new JAXBElement<Serializable>(SchemaConstants.C_PARAM_VALUE,
  // Serializable.class, value));
  //			} else if (XmlTypeConverter.canConvert(value.getClass())) {
  ////				try {
  ////					entryType.setEntryValue(new JXmlTypeConverter.toXsdElement(value,
  // SchemaConstants.C_PARAM_VALUE, doc, true));
  ////				} catch (SchemaException e) {
  ////					LOGGER.error("Cannot convert value {} to XML: {}",value,e.getMessage());
  ////					setUnknownJavaObjectEntry(entryType, value);
  ////				}
  //			} else if (value instanceof Element || value instanceof JAXBElement<?>) {
  //				entryType.setEntryValue((JAXBElement<?>) value);
  //			// FIXME: this is really bad code ... it means that 'our' JAXB object should be put as is
  //			} else if
  // ("com.evolveum.midpoint.xml.ns._public.common.common_2".equals(value.getClass().getPackage().getName())) {
  //				JAXBElement<Object> o = new JAXBElement<Object>(SchemaConstants.C_PARAM_VALUE, Object.class,
  // value);
  //				entryType.setEntryValue(o);
  //			} else {
  //				setUnknownJavaObjectEntry(entryType, value);
  //			}
  //		}
  //		return entryType;
  //	}
  //
  //	private void setObjectReferenceEntry(EntryType entryType, ObjectType objectType) {
  //		ObjectReferenceType objRefType = new ObjectReferenceType();
  //		objRefType.setOid(objectType.getOid());
  //		ObjectTypes type = ObjectTypes.getObjectType(objectType.getClass());
  //		if (type != null) {
  //			objRefType.setType(type.getTypeQName());
  //		}
  //		JAXBElement<ObjectReferenceType> element = new JAXBElement<ObjectReferenceType>(
  //				SchemaConstants.C_OBJECT_REF, ObjectReferenceType.class, objRefType);
  ////		entryType.setAny(element);
  //	}
  //
  //	private void setUnknownJavaObjectEntry(EntryType entryType, Serializable value) {
  //		UnknownJavaObjectType ujo = new UnknownJavaObjectType();
  //		ujo.setClazz(value.getClass().getName());
  //		ujo.setToString(value.toString());
  //		entryType.setEntryValue(new ObjectFactory().createUnknownJavaObject(ujo));
  //	}

  public void summarize() {
    Iterator<OperationResult> iterator = getSubresults().iterator();
    while (iterator.hasNext()) {
      OperationResult subresult = iterator.next();
      if (subresult.getCount() > 1) {
        // Already summarized
        continue;
      }
      if (subresult.isError() && summarizeErrors) {
        // go on
      } else if (subresult.isPartialError() && summarizePartialErrors) {
        // go on
      } else if (subresult.isSuccess() && summarizeSuccesses) {
        // go on
      } else {
        continue;
      }
      OperationResult similar = findSimilarSubresult(subresult);
      if (similar == null) {
        // Nothing to summarize to
        continue;
      }
      merge(similar, subresult);
      iterator.remove();
    }

    // subresult stripping if necessary
    // we strip subresults that have same operation name and status, if there are more of them than
    // threshold
    Map<OperationStatusKey, Integer> counter = new HashMap<OperationStatusKey, Integer>();
    iterator = getSubresults().iterator();
    while (iterator.hasNext()) {
      OperationResult sr = iterator.next();
      OperationStatusKey key = new OperationStatusKey(sr.getOperation(), sr.getStatus());
      if (counter.containsKey(key)) {
        int count = counter.get(key);
        if (count > SUBRESULT_STRIP_THRESHOLD) {
          iterator.remove();
        } else {
          counter.put(key, ++count);
        }
      } else {
        counter.put(key, 1);
      }
    }
  }

  private void merge(OperationResult target, OperationResult source) {
    mergeMap(target.getParams(), source.getParams());
    mergeMap(target.getContext(), source.getContext());
    mergeMap(target.getReturns(), source.getReturns());
    target.incrementCount();
  }

  private void mergeMap(Map<String, Serializable> targetMap, Map<String, Serializable> sourceMap) {
    for (Entry<String, Serializable> targetEntry : targetMap.entrySet()) {
      String targetKey = targetEntry.getKey();
      Serializable targetValue = targetEntry.getValue();
      if (targetValue != null && targetValue instanceof VariousValues) {
        continue;
      }
      Serializable sourceValue = sourceMap.get(targetKey);
      if (MiscUtil.equals(targetValue, sourceValue)) {
        // Entries match, nothing to do
        continue;
      }
      // Entries do not match. The target entry needs to be marked as VariousValues
      targetEntry.setValue(new VariousValues());
    }
    for (Entry<String, Serializable> sourceEntry : sourceMap.entrySet()) {
      String sourceKey = sourceEntry.getKey();
      if (!targetMap.containsKey(sourceKey)) {
        targetMap.put(sourceKey, new VariousValues());
      }
    }
  }

  private OperationResult findSimilarSubresult(OperationResult subresult) {
    OperationResult similar = null;
    for (OperationResult sub : getSubresults()) {
      if (sub == subresult) {
        continue;
      }
      if (!sub.operation.equals(subresult.operation)) {
        continue;
      }
      if (sub.status != subresult.status) {
        continue;
      }
      if (!MiscUtil.equals(sub.message, subresult.message)) {
        continue;
      }
      if (similar == null || (similar.count < sub.count)) {
        similar = sub;
      }
    }
    return similar;
  }

  /**
   * Removes all the successful minor results. Also checks if the result is roughly consistent and
   * complete. (e.g. does not have unknown operation status, etc.)
   */
  public void cleanupResult() {
    cleanupResult(null);
  }

  /**
   * Removes all the successful minor results. Also checks if the result is roughly consistent and
   * complete. (e.g. does not have unknown operation status, etc.)
   *
   * <p>The argument "e" is for easier use of the cleanup in the exceptions handlers. The original
   * exception is passed to the IAE that this method produces for easier debugging.
   */
  public void cleanupResult(Throwable e) {
    if (status == OperationResultStatus.UNKNOWN) {
      LOGGER.error(
          "Attempt to cleanup result of operation " + operation + " that is still UNKNOWN:\n{}",
          this.debugDump());
      throw new IllegalStateException(
          "Attempt to cleanup result of operation " + operation + " that is still UNKNOWN");
    }
    if (subresults == null) {
      return;
    }
    Iterator<OperationResult> iterator = subresults.iterator();
    while (iterator.hasNext()) {
      OperationResult subresult = iterator.next();
      if (subresult.getStatus() == OperationResultStatus.UNKNOWN) {
        String message =
            "Subresult "
                + subresult.getOperation()
                + " of operation "
                + operation
                + " is still UNKNOWN during cleanup";
        LOGGER.error("{}:\n{}", message, this.debugDump(), e);
        if (e == null) {
          throw new IllegalStateException(message);
        } else {
          throw new IllegalStateException(message + "; during handling of exception " + e, e);
        }
      }
      if (subresult.canCleanup()) {
        iterator.remove();
      }
    }
  }

  private boolean canCleanup() {
    if (!minor) {
      return false;
    }
    return status == OperationResultStatus.SUCCESS
        || status == OperationResultStatus.NOT_APPLICABLE;
  }

  @Override
  public String debugDump() {
    return debugDump(0);
  }

  @Override
  public String debugDump(int indent) {
    StringBuilder sb = new StringBuilder();
    dumpIndent(sb, indent, true);
    return sb.toString();
  }

  public String dump(boolean withStack) {
    StringBuilder sb = new StringBuilder();
    dumpIndent(sb, 0, withStack);
    return sb.toString();
  }

  private void dumpIndent(StringBuilder sb, int indent, boolean printStackTrace) {
    for (int i = 0; i < indent; i++) {
      sb.append(INDENT_STRING);
    }
    sb.append("*op* ");
    sb.append(operation);
    sb.append(", st: ");
    sb.append(status);
    if (minor) {
      sb.append(", MINOR");
    }
    sb.append(", msg: ");
    sb.append(message);
    if (count > 1) {
      sb.append(" x");
      sb.append(count);
    }
    sb.append("\n");

    for (Map.Entry<String, Serializable> entry : getParams().entrySet()) {
      for (int i = 0; i < indent + 2; i++) {
        sb.append(INDENT_STRING);
      }
      sb.append("[p]");
      sb.append(entry.getKey());
      sb.append("=");
      sb.append(dumpEntry(indent + 2, entry.getValue()));
      sb.append("\n");
    }

    for (Map.Entry<String, Serializable> entry : getContext().entrySet()) {
      for (int i = 0; i < indent + 2; i++) {
        sb.append(INDENT_STRING);
      }
      sb.append("[c]");
      sb.append(entry.getKey());
      sb.append("=");
      sb.append(dumpEntry(indent + 2, entry.getValue()));
      sb.append("\n");
    }

    for (Map.Entry<String, Serializable> entry : getReturns().entrySet()) {
      for (int i = 0; i < indent + 2; i++) {
        sb.append(INDENT_STRING);
      }
      sb.append("[r]");
      sb.append(entry.getKey());
      sb.append("=");
      sb.append(dumpEntry(indent + 2, entry.getValue()));
      sb.append("\n");
    }

    for (String line : details) {
      for (int i = 0; i < indent + 2; i++) {
        sb.append(INDENT_STRING);
      }
      sb.append("[d]");
      sb.append(line);
      sb.append("\n");
    }

    if (cause != null) {
      for (int i = 0; i < indent + 2; i++) {
        sb.append(INDENT_STRING);
      }
      sb.append("[cause]");
      sb.append(cause.getClass().getSimpleName());
      sb.append(":");
      sb.append(cause.getMessage());
      sb.append("\n");
      if (printStackTrace) {
        dumpStackTrace(sb, cause.getStackTrace(), indent + 4);
        dumpInnerCauses(sb, cause.getCause(), indent + 3);
      }
    }

    for (OperationResult sub : getSubresults()) {
      sub.dumpIndent(sb, indent + 1, printStackTrace);
    }
  }

  private String dumpEntry(int indent, Serializable value) {
    if (value instanceof Element) {
      Element element = (Element) value;
      if (SchemaConstants.C_VALUE.equals(DOMUtil.getQName(element))) {
        try {
          String cvalue = null;
          if (value == null) {
            cvalue = "null";
          } else if (value instanceof Element) {
            cvalue = SchemaDebugUtil.prettyPrint(XmlTypeConverter.toJavaValue((Element) value));
          } else {
            cvalue = SchemaDebugUtil.prettyPrint(value);
          }
          return DebugUtil.fixIndentInMultiline(indent, INDENT_STRING, cvalue);
        } catch (Exception e) {
          return DebugUtil.fixIndentInMultiline(
              indent, INDENT_STRING, "value: " + element.getTextContent());
        }
      }
    }
    return DebugUtil.fixIndentInMultiline(
        indent, INDENT_STRING, SchemaDebugUtil.prettyPrint(value));
  }

  private void dumpInnerCauses(StringBuilder sb, Throwable innerCause, int indent) {
    if (innerCause == null) {
      return;
    }
    for (int i = 0; i < indent; i++) {
      sb.append(INDENT_STRING);
    }
    sb.append("Caused by ");
    sb.append(innerCause.getClass().getName());
    sb.append(": ");
    sb.append(innerCause.getMessage());
    sb.append("\n");
    dumpStackTrace(sb, innerCause.getStackTrace(), indent + 1);
    dumpInnerCauses(sb, innerCause.getCause(), indent);
  }

  private static void dumpStackTrace(StringBuilder sb, StackTraceElement[] stackTrace, int indent) {
    for (int i = 0; i < stackTrace.length; i++) {
      for (int j = 0; j < indent; j++) {
        sb.append(INDENT_STRING);
      }
      StackTraceElement element = stackTrace[i];
      sb.append(element.toString());
      sb.append("\n");
    }
  }

  public void setBackgroundTaskOid(String oid) {
    addReturn(RETURN_BACKGROUND_TASK_OID, oid);
  }

  public String getBackgroundTaskOid() {
    Object oid = getReturns().get(RETURN_BACKGROUND_TASK_OID);
    return oid != null ? String.valueOf(oid) : null;
  }

  // primitive implementation - uncomment it if needed
  //    public OperationResult clone() {
  //        return CloneUtil.clone(this);
  //    }

  private static class OperationStatusKey {

    private String operation;
    private OperationResultStatus status;

    private OperationStatusKey(String operation, OperationResultStatus status) {
      this.operation = operation;
      this.status = status;
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;

      OperationStatusKey that = (OperationStatusKey) o;

      if (operation != null ? !operation.equals(that.operation) : that.operation != null)
        return false;
      if (status != that.status) return false;

      return true;
    }

    @Override
    public int hashCode() {
      int result = operation != null ? operation.hashCode() : 0;
      result = 31 * result + (status != null ? status.hashCode() : 0);
      return result;
    }
  }

  public OperationResult clone() {
    OperationResult clone = new OperationResult(operation);

    clone.status = status;
    clone.params = CloneUtil.clone(params);
    clone.context = CloneUtil.clone(context);
    clone.returns = CloneUtil.clone(returns);
    clone.token = token;
    clone.messageCode = messageCode;
    clone.message = message;
    clone.localizationMessage = localizationMessage;
    clone.localizationArguments = CloneUtil.clone(localizationArguments);
    clone.cause = CloneUtil.clone(cause);
    clone.count = count;
    if (subresults != null) {
      clone.subresults = new ArrayList<>(subresults.size());
      for (OperationResult subresult : subresults) {
        if (subresult != null) {
          clone.subresults.add(subresult.clone());
        }
      }
    }
    clone.details = CloneUtil.clone(details);
    clone.summarizeErrors = summarizeErrors;
    clone.summarizePartialErrors = summarizePartialErrors;
    clone.summarizeSuccesses = summarizeSuccesses;
    clone.minor = minor;

    return clone;
  }
}
Example #23
0
/** @author lazyman */
public final class RUtil {

  /**
   * This constant is used for mapping type for {@link javax.persistence.Lob} fields. {@link
   * org.hibernate.type.MaterializedClobType} was not working properly with PostgreSQL, causing TEXT
   * types (clobs) to be saved not in table row but somewhere else and it always messed up UTF-8
   * encoding
   */
  public static final String LOB_STRING_TYPE = "org.hibernate.type.StringClobType";

  public static final int COLUMN_LENGTH_QNAME = 157;

  public static final String QNAME_DELIMITER = "#";

  /** This constant is used for oid column size in database. */
  public static final int COLUMN_LENGTH_OID = 36;

  /** This namespace is used for wrapping xml parts of objects during save to database. */
  public static final String NS_SQL_REPO =
      "http://midpoint.evolveum.com/xml/ns/fake/sqlRepository-1.xsd";

  public static final String SQL_REPO_OBJECT = "sqlRepoObject";
  public static final QName CUSTOM_OBJECT = new QName(NS_SQL_REPO, SQL_REPO_OBJECT);

  private static final Trace LOGGER = TraceManager.getTrace(RUtil.class);

  private RUtil() {}

  public static <T extends Objectable> void revive(Objectable object, PrismContext prismContext)
      throws DtoTranslationException {
    try {
      prismContext.adopt(object);
    } catch (SchemaException ex) {
      throw new DtoTranslationException(ex.getMessage(), ex);
    }
  }

  public static <T> String toRepo(
      ItemDefinition parentDefinition, QName itemName, T value, PrismContext prismContext)
      throws SchemaException, JAXBException {
    if (value == null) {
      return null;
    }

    if (value instanceof Objectable) {
      return prismContext.serializeObjectToString(
          ((Objectable) value).asPrismObject(), PrismContext.LANG_XML);
    }

    ItemDefinition definition = null;
    if (parentDefinition instanceof PrismContainerDefinition) {
      definition = ((PrismContainerDefinition) parentDefinition).findItemDefinition(itemName);
      if (definition == null) {
        definition = parentDefinition;
      }
    } else {
      definition = parentDefinition;
    }

    return ValueSerializationUtil.serializeValue(
        value,
        definition,
        itemName,
        parentDefinition.getName(),
        prismContext,
        PrismContext.LANG_XML);
  }

  public static Element createFakeParentElement() {
    return DOMUtil.createElement(DOMUtil.getDocument(), CUSTOM_OBJECT);
  }

  public static <T> Set<T> listToSet(List<T> list) {
    if (list == null || list.isEmpty()) {
      return null;
    }
    return new HashSet<>(list);
  }

  public static Set<RPolyString> listPolyToSet(List<PolyStringType> list) {
    if (list == null || list.isEmpty()) {
      return null;
    }

    Set<RPolyString> set = new HashSet<>();
    for (PolyStringType str : list) {
      set.add(RPolyString.copyFromJAXB(str));
    }
    return set;
  }

  public static List<ObjectReferenceType> safeSetReferencesToList(
      Set<? extends RObjectReference> set, PrismContext prismContext) {

    if (set == null || set.isEmpty()) {
      return new ArrayList<>();
    }

    List<ObjectReferenceType> list = new ArrayList<>();
    for (RObjectReference str : set) {
      ObjectReferenceType ort = new ObjectReferenceType();
      RObjectReference.copyToJAXB(str, ort, prismContext);
      list.add(ort);
    }
    return list;
  }

  public static Set safeListReferenceToSet(
      List<ObjectReferenceType> list,
      PrismContext prismContext,
      RObject owner,
      RReferenceOwner refOwner) {
    Set<RObjectReference> set = new HashSet<>();
    if (list == null || list.isEmpty()) {
      return set;
    }

    for (ObjectReferenceType ref : list) {
      RObjectReference rRef = RUtil.jaxbRefToRepo(ref, prismContext, owner, refOwner);
      if (rRef != null) {
        set.add(rRef);
      }
    }
    return set;
  }

  public static RObjectReference jaxbRefToRepo(
      ObjectReferenceType reference,
      PrismContext prismContext,
      RObject owner,
      RReferenceOwner refOwner) {
    if (reference == null) {
      return null;
    }
    Validate.notNull(owner, "Owner of reference must not be null.");
    Validate.notNull(refOwner, "Reference owner of reference must not be null.");
    Validate.notEmpty(reference.getOid(), "Target oid reference must not be null.");

    RObjectReference repoRef = RReferenceOwner.createObjectReference(refOwner);
    repoRef.setOwner(owner);
    RObjectReference.copyFromJAXB(reference, repoRef, prismContext);

    return repoRef;
  }

  public static REmbeddedReference jaxbRefToEmbeddedRepoRef(
      ObjectReferenceType jaxb, PrismContext prismContext) {
    if (jaxb == null) {
      return null;
    }
    REmbeddedReference ref = new REmbeddedReference();
    REmbeddedReference.copyFromJAXB(jaxb, ref, prismContext);

    return ref;
  }

  public static Integer getIntegerFromString(String val) {
    if (val == null || !val.matches("[0-9]+")) {
      return null;
    }

    return Integer.parseInt(val);
  }

  /**
   * This method is used to override "hasIdentifierMapper" in EntityMetaModels of entities which
   * have composite id and class defined for it. It's workaround for bug as found in forum
   * https://forum.hibernate.org/viewtopic.php?t=978915&highlight=
   *
   * @param sessionFactory
   */
  public static void fixCompositeIDHandling(SessionFactory sessionFactory) {
    fixCompositeIdentifierInMetaModel(sessionFactory, RObjectDeltaOperation.class);

    fixCompositeIdentifierInMetaModel(sessionFactory, ROExtDate.class);
    fixCompositeIdentifierInMetaModel(sessionFactory, ROExtString.class);
    fixCompositeIdentifierInMetaModel(sessionFactory, ROExtPolyString.class);
    fixCompositeIdentifierInMetaModel(sessionFactory, ROExtReference.class);
    fixCompositeIdentifierInMetaModel(sessionFactory, ROExtLong.class);

    fixCompositeIdentifierInMetaModel(sessionFactory, RAssignmentExtension.class);
    fixCompositeIdentifierInMetaModel(sessionFactory, RAExtDate.class);
    fixCompositeIdentifierInMetaModel(sessionFactory, RAExtString.class);
    fixCompositeIdentifierInMetaModel(sessionFactory, RAExtPolyString.class);
    fixCompositeIdentifierInMetaModel(sessionFactory, RAExtReference.class);
    fixCompositeIdentifierInMetaModel(sessionFactory, RAExtLong.class);

    fixCompositeIdentifierInMetaModel(sessionFactory, RObjectReference.class);
    for (RReferenceOwner owner : RReferenceOwner.values()) {
      fixCompositeIdentifierInMetaModel(sessionFactory, owner.getClazz());
    }

    fixCompositeIdentifierInMetaModel(sessionFactory, RAssignmentReference.class);
    for (RCReferenceOwner owner : RCReferenceOwner.values()) {
      fixCompositeIdentifierInMetaModel(sessionFactory, owner.getClazz());
    }

    fixCompositeIdentifierInMetaModel(sessionFactory, RAssignment.class);
    fixCompositeIdentifierInMetaModel(sessionFactory, RExclusion.class);
    fixCompositeIdentifierInMetaModel(sessionFactory, RTrigger.class);
    for (RObjectType type : ClassMapper.getKnownTypes()) {
      fixCompositeIdentifierInMetaModel(sessionFactory, type.getClazz());
    }
  }

  private static void fixCompositeIdentifierInMetaModel(
      SessionFactory sessionFactory, Class clazz) {
    ClassMetadata classMetadata = sessionFactory.getClassMetadata(clazz);
    if (classMetadata instanceof AbstractEntityPersister) {
      AbstractEntityPersister persister = (AbstractEntityPersister) classMetadata;
      EntityMetamodel model = persister.getEntityMetamodel();
      IdentifierProperty identifier = model.getIdentifierProperty();

      try {
        Field field = IdentifierProperty.class.getDeclaredField("hasIdentifierMapper");
        field.setAccessible(true);
        field.set(identifier, true);
        field.setAccessible(false);
      } catch (Exception ex) {
        throw new SystemException(
            "Attempt to fix entity meta model with hack failed, reason: " + ex.getMessage(), ex);
      }
    }
  }

  public static void copyResultFromJAXB(
      ItemDefinition parentDef,
      QName itemName,
      OperationResultType jaxb,
      OperationResult repo,
      PrismContext prismContext)
      throws DtoTranslationException {
    Validate.notNull(repo, "Repo object must not be null.");

    if (jaxb == null) {
      return;
    }

    repo.setStatus(getRepoEnumValue(jaxb.getStatus(), ROperationResultStatus.class));
    if (repo instanceof OperationResultFull) {
      try {
        ((OperationResultFull) repo)
            .setFullResult(RUtil.toRepo(parentDef, itemName, jaxb, prismContext));
      } catch (Exception ex) {
        throw new DtoTranslationException(ex.getMessage(), ex);
      }
    }
  }

  public static String computeChecksum(Object... objects) {
    StringBuilder builder = new StringBuilder();
    for (Object object : objects) {
      if (object == null) {
        continue;
      }

      builder.append(object.toString());
    }

    return DigestUtils.md5Hex(builder.toString());
  }

  public static <T extends SchemaEnum> T getRepoEnumValue(Object object, Class<T> type) {
    if (object == null) {
      return null;
    }
    Object[] values = type.getEnumConstants();
    for (Object value : values) {
      T schemaEnum = (T) value;
      if (schemaEnum.getSchemaValue().equals(object)) {
        return schemaEnum;
      }
    }

    throw new IllegalArgumentException(
        "Unknown value '"
            + object
            + "' of type '"
            + object.getClass()
            + "', can't translate to '"
            + type
            + "'.");
  }

  public static String qnameToString(QName qname) {
    StringBuilder sb = new StringBuilder();
    if (qname != null) {
      sb.append(qname.getNamespaceURI());
    }
    sb.append(QNAME_DELIMITER);
    if (qname != null) {
      sb.append(qname.getLocalPart());
    }

    return sb.toString();
  }

  public static QName stringToQName(String text) {
    if (StringUtils.isEmpty(text)) {
      return null;
    }

    int index = text.lastIndexOf(QNAME_DELIMITER);
    String namespace = StringUtils.left(text, index);
    String localPart = StringUtils.right(text, text.length() - index - 1);

    if (StringUtils.isEmpty(localPart)) {
      return null;
    }

    return new QName(namespace, localPart);
  }

  public static Long toLong(Short s) {
    if (s == null) {
      return null;
    }

    return s.longValue();
  }

  public static Short toShort(Long l) {
    if (l == null) {
      return null;
    }

    if (l > Short.MAX_VALUE || l < Short.MIN_VALUE) {
      throw new IllegalArgumentException("Couldn't cast value to short " + l);
    }

    return l.shortValue();
  }

  public static String getDebugString(RObject object) {
    StringBuilder sb = new StringBuilder();
    if (object.getName() != null) {
      sb.append(object.getName().getOrig());
    } else {
      sb.append("null");
    }
    sb.append('(').append(object.getOid()).append(')');

    return sb.toString();
  }

  public static String getTableName(Class hqlType) {
    MidPointNamingStrategy namingStrategy = new MidPointNamingStrategy();
    return namingStrategy.classToTableName(hqlType.getSimpleName());
  }

  public static byte[] getByteArrayFromXml(String xml, boolean compress) {
    byte[] array;

    GZIPOutputStream gzip = null;
    try {
      if (compress) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        gzip = new GZIPOutputStream(out);
        gzip.write(xml.getBytes("utf-8"));
        gzip.close();
        out.close();

        array = out.toByteArray();
      } else {
        array = xml.getBytes("utf-8");
      }
    } catch (Exception ex) {
      throw new SystemException("Couldn't save full xml object, reason: " + ex.getMessage(), ex);
    } finally {
      IOUtils.closeQuietly(gzip);
    }

    return array;
  }

  public static String getXmlFromByteArray(byte[] array, boolean compressed) {
    String xml;

    GZIPInputStream gzip = null;
    try {
      if (compressed) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        gzip = new GZIPInputStream(new ByteArrayInputStream(array));
        IOUtils.copy(gzip, out);
        xml = new String(out.toByteArray(), "utf-8");
      } else {
        xml = new String(array, "utf-8");
      }
    } catch (Exception ex) {
      throw new SystemException(
          "Couldn't read data from full object column, reason: " + ex.getMessage(), ex);
    } finally {
      IOUtils.closeQuietly(gzip);
    }

    return xml;
  }

  public static OrgFilter findOrgFilter(ObjectQuery query) {
    return query != null ? findOrgFilter(query.getFilter()) : null;
  }

  public static OrgFilter findOrgFilter(ObjectFilter filter) {
    if (filter == null) {
      return null;
    }

    if (filter instanceof OrgFilter) {
      return (OrgFilter) filter;
    }

    if (filter instanceof LogicalFilter) {
      LogicalFilter logical = (LogicalFilter) filter;
      for (ObjectFilter f : logical.getConditions()) {
        OrgFilter o = findOrgFilter(f);
        if (o != null) {
          return o;
        }
      }
    }

    return null;
  }
}
/**
 * Processor to handle everything about focus: values, assignments, etc.
 *
 * @author Radovan Semancik
 */
@Component
public class FocusProcessor {

  private static final Trace LOGGER = TraceManager.getTrace(FocusProcessor.class);

  private PrismContainerDefinition<ActivationType> activationDefinition;

  @Autowired(required = true)
  private InboundProcessor inboundProcessor;

  @Autowired(required = true)
  private AssignmentProcessor assignmentProcessor;

  @Autowired(required = true)
  private ObjectTemplateProcessor objectTemplateProcessor;

  @Autowired(required = true)
  private MappingFactory mappingFactory;

  @Autowired(required = true)
  private PrismContext prismContext;

  @Autowired(required = true)
  private PasswordPolicyProcessor passwordPolicyProcessor;

  @Autowired(required = true)
  private ModelObjectResolver modelObjectResolver;

  @Autowired(required = true)
  private ActivationComputer activationComputer;

  @Autowired(required = true)
  private ExpressionFactory expressionFactory;

  @Autowired(required = true)
  @Qualifier("cacheRepositoryService")
  private transient RepositoryService cacheRepositoryService;

  @Autowired(required = true)
  private MappingEvaluationHelper mappingHelper;

  <O extends ObjectType, F extends FocusType> void processFocus(
      LensContext<O> context,
      String activityDescription,
      XMLGregorianCalendar now,
      Task task,
      OperationResult result)
      throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException,
          PolicyViolationException, ObjectAlreadyExistsException, CommunicationException,
          ConfigurationException, SecurityViolationException {

    LensFocusContext<O> focusContext = context.getFocusContext();
    if (focusContext == null) {
      return;
    }

    if (!FocusType.class.isAssignableFrom(focusContext.getObjectTypeClass())) {
      // We can do this only for FocusType objects.
      return;
    }

    LensContext<F> fContext = (LensContext<F>) context;
    LensFocusContext<F> fFocusContext = fContext.getFocusContext();

    processFocusFocus((LensContext<F>) context, activityDescription, now, task, result);
  }

  private <F extends FocusType> void processFocusFocus(
      LensContext<F> context,
      String activityDescription,
      XMLGregorianCalendar now,
      Task task,
      OperationResult result)
      throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException,
          PolicyViolationException, ObjectAlreadyExistsException, CommunicationException,
          ConfigurationException, SecurityViolationException {

    LensFocusContext<F> focusContext = context.getFocusContext();
    ObjectTemplateType objectTemplate = context.getFocusTemplate();

    boolean resetOnRename = true; // This is fixed now. TODO: make it configurable
    int maxIterations = 0;
    IterationSpecificationType iterationSpecificationType = null;
    if (objectTemplate != null) {
      iterationSpecificationType = objectTemplate.getIteration();
      maxIterations = LensUtil.determineMaxIterations(iterationSpecificationType);
    }
    int iteration = focusContext.getIteration();
    String iterationToken = focusContext.getIterationToken();
    boolean wasResetIterationCounter = false;

    PrismObject<F> focusCurrent = focusContext.getObjectCurrent();
    if (focusCurrent != null && iterationToken == null) {
      Integer focusIteration = focusCurrent.asObjectable().getIteration();
      if (focusIteration != null) {
        iteration = focusIteration;
      }
      iterationToken = focusCurrent.asObjectable().getIterationToken();
    }

    while (true) {

      ObjectTypeTemplateType objectPolicyConfigurationType =
          focusContext.getObjectPolicyConfigurationType();
      if (objectPolicyConfigurationType != null
          && BooleanUtils.isTrue(objectPolicyConfigurationType.isOidNameBoundMode())) {
        // Generate the name now - unless it is already present
        PrismObject<F> focusNew = focusContext.getObjectNew();
        if (focusNew != null) {
          PolyStringType focusNewName = focusNew.asObjectable().getName();
          if (focusNewName == null) {
            String newName = focusNew.getOid();
            if (newName == null) {
              newName = OidUtil.generateOid();
            }
            LOGGER.trace("Generating new name (bound to OID): {}", newName);
            PrismObjectDefinition<F> focusDefinition = focusContext.getObjectDefinition();
            PrismPropertyDefinition<PolyString> focusNameDef =
                focusDefinition.findPropertyDefinition(FocusType.F_NAME);
            PropertyDelta<PolyString> nameDelta =
                focusNameDef.createEmptyDelta(new ItemPath(FocusType.F_NAME));
            nameDelta.setValueToReplace(
                new PrismPropertyValue<PolyString>(
                    new PolyString(newName), OriginType.USER_POLICY, null));
            focusContext.swallowToSecondaryDelta(nameDelta);
            focusContext.recompute();
          }
        }
      }

      ExpressionVariables variables =
          Utils.getDefaultExpressionVariables(
              focusContext.getObjectNew(), null, null, null, context.getSystemConfiguration());
      if (iterationToken == null) {
        iterationToken =
            LensUtil.formatIterationToken(
                context,
                focusContext,
                iterationSpecificationType,
                iteration,
                expressionFactory,
                variables,
                task,
                result);
      }

      // We have to remember the token and iteration in the context.
      // The context can be recomputed several times. But we always want
      // to use the same iterationToken if possible. If there is a random
      // part in the iterationToken expression that we need to avoid recomputing
      // the token otherwise the value can change all the time (even for the same inputs).
      // Storing the token in the secondary delta is not enough because secondary deltas can be
      // dropped
      // if the context is re-projected.
      focusContext.setIteration(iteration);
      focusContext.setIterationToken(iterationToken);
      LOGGER.trace(
          "Focus {} processing, iteration {}, token '{}'",
          new Object[] {focusContext.getHumanReadableName(), iteration, iterationToken});

      String conflictMessage;
      if (!LensUtil.evaluateIterationCondition(
          context,
          focusContext,
          iterationSpecificationType,
          iteration,
          iterationToken,
          true,
          expressionFactory,
          variables,
          task,
          result)) {

        conflictMessage = "pre-iteration condition was false";
        LOGGER.debug(
            "Skipping iteration {}, token '{}' for {} because the pre-iteration condition was false",
            new Object[] {iteration, iterationToken, focusContext.getHumanReadableName()});
      } else {

        // INBOUND

        if (consistencyChecks) context.checkConsistence();
        // Loop through the account changes, apply inbound expressions
        inboundProcessor.processInbound(context, now, task, result);
        if (consistencyChecks) context.checkConsistence();
        context.recomputeFocus();
        LensUtil.traceContext(LOGGER, activityDescription, "inbound", false, context, false);
        if (consistencyChecks) context.checkConsistence();

        // ACTIVATION

        processActivation(context, now, result);

        // OBJECT TEMPLATE (before assignments)

        objectTemplateProcessor.processTemplate(
            context,
            ObjectTemplateMappingEvaluationPhaseType.BEFORE_ASSIGNMENTS,
            now,
            task,
            result);

        // ASSIGNMENTS

        assignmentProcessor.processAssignmentsProjections(context, now, task, result);
        assignmentProcessor.processOrgAssignments(context, result);
        context.recompute();

        assignmentProcessor.checkForAssignmentConflicts(context, result);

        // OBJECT TEMPLATE (after assignments)

        objectTemplateProcessor.processTemplate(
            context, ObjectTemplateMappingEvaluationPhaseType.AFTER_ASSIGNMENTS, now, task, result);
        context.recompute();

        // PASSWORD POLICY

        passwordPolicyProcessor.processPasswordPolicy(focusContext, context, result);

        // Processing done, check for success

        if (resetOnRename && !wasResetIterationCounter && willResetIterationCounter(focusContext)) {
          // Make sure this happens only the very first time during the first recompute.
          // Otherwise it will always change the token (especially if the token expression has a
          // random part)
          // hence the focusContext.getIterationToken() == null
          wasResetIterationCounter = true;
          if (iteration != 0) {
            iteration = 0;
            iterationToken = null;
            LOGGER.trace("Resetting iteration counter and token because rename was detected");
            cleanupContext(focusContext);
            continue;
          }
        }

        PrismObject<F> previewObjectNew = focusContext.getObjectNew();
        if (previewObjectNew == null) {
          // this must be delete
        } else {
          // Explicitly check for name. The checker would check for this also. But checking it here
          // will produce better error message
          PolyStringType objectName = previewObjectNew.asObjectable().getName();
          if (objectName == null || objectName.getOrig().isEmpty()) {
            throw new SchemaException(
                "No name in new object "
                    + objectName
                    + " as produced by template "
                    + objectTemplate
                    + " in iteration "
                    + iteration
                    + ", we cannot process an object without a name");
          }
        }

        // Check if iteration constraints are OK
        FocusConstraintsChecker<F> checker = new FocusConstraintsChecker<>();
        checker.setPrismContext(prismContext);
        checker.setContext(context);
        checker.setRepositoryService(cacheRepositoryService);
        checker.check(previewObjectNew, result);
        if (checker.isSatisfiesConstraints()) {
          LOGGER.trace(
              "Current focus satisfies uniqueness constraints. Iteration {}, token '{}'",
              iteration,
              iterationToken);

          if (LensUtil.evaluateIterationCondition(
              context,
              focusContext,
              iterationSpecificationType,
              iteration,
              iterationToken,
              false,
              expressionFactory,
              variables,
              task,
              result)) {
            // stop the iterations
            break;
          } else {
            conflictMessage = "post-iteration condition was false";
            LOGGER.debug(
                "Skipping iteration {}, token '{}' for {} because the post-iteration condition was false",
                new Object[] {iteration, iterationToken, focusContext.getHumanReadableName()});
          }
        } else {
          LOGGER.trace(
              "Current focus does not satisfy constraints. Conflicting object: {}; iteration={}, maxIterations={}",
              new Object[] {checker.getConflictingObject(), iteration, maxIterations});
          conflictMessage = checker.getMessages();
        }

        if (!wasResetIterationCounter) {
          wasResetIterationCounter = true;
          if (iteration != 0) {
            iterationToken = null;
            iteration = 0;
            LOGGER.trace("Resetting iteration counter and token after conflict");
            cleanupContext(focusContext);
            continue;
          }
        }
      }

      // Next iteration
      iteration++;
      iterationToken = null;
      if (iteration > maxIterations) {
        StringBuilder sb = new StringBuilder();
        if (iteration == 1) {
          sb.append("Error processing ");
        } else {
          sb.append("Too many iterations (" + iteration + ") for ");
        }
        sb.append(focusContext.getHumanReadableName());
        if (iteration == 1) {
          sb.append(": constraint violation: ");
        } else {
          sb.append(": cannot determine values that satisfy constraints: ");
        }
        if (conflictMessage != null) {
          sb.append(conflictMessage);
        }
        throw new ObjectAlreadyExistsException(sb.toString());
      }
      cleanupContext(focusContext);
    }

    addIterationTokenDeltas(focusContext, iteration, iterationToken);
    if (consistencyChecks) context.checkConsistence();
  }

  private <F extends FocusType> boolean willResetIterationCounter(LensFocusContext<F> focusContext)
      throws SchemaException {
    ObjectDelta<F> focusDelta = focusContext.getDelta();
    if (focusDelta == null) {
      return false;
    }
    if (focusContext.isAdd() || focusContext.isDelete()) {
      return false;
    }
    if (focusDelta.findPropertyDelta(FocusType.F_ITERATION) != null) {
      // there was a reset already in previous projector runs
      return false;
    }
    // Check for rename
    PropertyDelta<Object> nameDelta = focusDelta.findPropertyDelta(new ItemPath(FocusType.F_NAME));
    return nameDelta != null;
  }

  /** Remove the intermediate results of values processing such as secondary deltas. */
  private <F extends FocusType> void cleanupContext(LensFocusContext<F> focusContext)
      throws SchemaException {
    // We must NOT clean up activation computation. This has happened before, it will not happen
    // again
    // and it does not depend on iteration
    LOGGER.trace("Cleaning up focus context");
    focusContext.setProjectionWaveSecondaryDelta(null);

    focusContext.clearIntermediateResults();
    focusContext.recompute();
  }

  private <F extends FocusType> void processActivation(
      LensContext<F> context, XMLGregorianCalendar now, OperationResult result)
      throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException,
          PolicyViolationException {
    LensFocusContext<F> focusContext = context.getFocusContext();

    if (focusContext.isDelete()) {
      LOGGER.trace("Skipping processing of focus activation: focus delete");
      return;
    }

    TimeIntervalStatusType validityStatusNew = null;
    TimeIntervalStatusType validityStatusOld = null;
    XMLGregorianCalendar validityChangeTimestamp = null;

    ActivationType activationNew = null;
    ActivationType activationOld = null;

    PrismObject<F> focusNew = focusContext.getObjectNew();
    if (focusNew != null) {
      activationNew = focusNew.asObjectable().getActivation();
      if (activationNew != null) {
        validityStatusNew = activationComputer.getValidityStatus(activationNew, now);
        validityChangeTimestamp = activationNew.getValidityChangeTimestamp();
      }
    }

    PrismObject<F> focusOld = focusContext.getObjectOld();
    if (focusOld != null) {
      activationOld = focusOld.asObjectable().getActivation();
      if (activationOld != null) {
        validityStatusOld =
            activationComputer.getValidityStatus(activationOld, validityChangeTimestamp);
      }
    }

    if (validityStatusOld == validityStatusNew) {
      // No change, (almost) no work
      if (validityStatusNew != null && activationNew.getValidityStatus() == null) {
        // There was no validity change. But the status is not recorded. So let's record it so it
        // can be used in searches.
        recordValidityDelta(focusContext, validityStatusNew, now);
      } else {
        LOGGER.trace(
            "Skipping validity processing because there was no change ({} -> {})",
            validityStatusOld,
            validityStatusNew);
      }
    } else {
      LOGGER.trace("Validity change {} -> {}", validityStatusOld, validityStatusNew);
      recordValidityDelta(focusContext, validityStatusNew, now);
    }

    ActivationStatusType effectiveStatusNew =
        activationComputer.getEffectiveStatus(
            activationNew, validityStatusNew, ActivationStatusType.DISABLED);
    ActivationStatusType effectiveStatusOld =
        activationComputer.getEffectiveStatus(
            activationOld, validityStatusOld, ActivationStatusType.DISABLED);

    if (effectiveStatusOld == effectiveStatusNew) {
      // No change, (almost) no work
      if (effectiveStatusNew != null
          && (activationNew == null || activationNew.getEffectiveStatus() == null)) {
        // There was no effective status change. But the status is not recorded. So let's record it
        // so it can be used in searches.
        recordEffectiveStatusDelta(focusContext, effectiveStatusNew, now);
      } else {
        if (focusContext.getPrimaryDelta() != null
            && focusContext
                .getPrimaryDelta()
                .hasItemDelta(SchemaConstants.PATH_ACTIVATION_ADMINISTRATIVE_STATUS)) {
          LOGGER.trace(
              "Forcing effective status delta even though there was no change ({} -> {}) because there is explicit administrativeStatus delta",
              effectiveStatusOld,
              effectiveStatusNew);
          // We need this to force the change down to the projections later in the activation
          // processor
          // some of the mappings will use effectiveStatus as a source, therefore there has to be a
          // delta for the mapping to work correctly
          recordEffectiveStatusDelta(focusContext, effectiveStatusNew, now);
        } else {
          LOGGER.trace(
              "Skipping effective status processing because there was no change ({} -> {})",
              effectiveStatusOld,
              effectiveStatusNew);
        }
      }
    } else {
      LOGGER.trace("Effective status change {} -> {}", effectiveStatusOld, effectiveStatusNew);
      recordEffectiveStatusDelta(focusContext, effectiveStatusNew, now);
    }
  }

  private <F extends ObjectType> void recordValidityDelta(
      LensFocusContext<F> focusContext,
      TimeIntervalStatusType validityStatusNew,
      XMLGregorianCalendar now)
      throws SchemaException {
    PrismContainerDefinition<ActivationType> activationDefinition = getActivationDefinition();

    PrismPropertyDefinition<TimeIntervalStatusType> validityStatusDef =
        activationDefinition.findPropertyDefinition(ActivationType.F_VALIDITY_STATUS);
    PropertyDelta<TimeIntervalStatusType> validityStatusDelta =
        validityStatusDef.createEmptyDelta(
            new ItemPath(UserType.F_ACTIVATION, ActivationType.F_VALIDITY_STATUS));
    if (validityStatusNew == null) {
      validityStatusDelta.setValueToReplace();
    } else {
      validityStatusDelta.setValueToReplace(
          new PrismPropertyValue<TimeIntervalStatusType>(
              validityStatusNew, OriginType.USER_POLICY, null));
    }
    focusContext.swallowToProjectionWaveSecondaryDelta(validityStatusDelta);

    PrismPropertyDefinition<XMLGregorianCalendar> validityChangeTimestampDef =
        activationDefinition.findPropertyDefinition(ActivationType.F_VALIDITY_CHANGE_TIMESTAMP);
    PropertyDelta<XMLGregorianCalendar> validityChangeTimestampDelta =
        validityChangeTimestampDef.createEmptyDelta(
            new ItemPath(UserType.F_ACTIVATION, ActivationType.F_VALIDITY_CHANGE_TIMESTAMP));
    validityChangeTimestampDelta.setValueToReplace(
        new PrismPropertyValue<XMLGregorianCalendar>(now, OriginType.USER_POLICY, null));
    focusContext.swallowToProjectionWaveSecondaryDelta(validityChangeTimestampDelta);
  }

  private <F extends ObjectType> void recordEffectiveStatusDelta(
      LensFocusContext<F> focusContext,
      ActivationStatusType effectiveStatusNew,
      XMLGregorianCalendar now)
      throws SchemaException {
    PrismContainerDefinition<ActivationType> activationDefinition = getActivationDefinition();

    PrismPropertyDefinition<ActivationStatusType> effectiveStatusDef =
        activationDefinition.findPropertyDefinition(ActivationType.F_EFFECTIVE_STATUS);
    PropertyDelta<ActivationStatusType> effectiveStatusDelta =
        effectiveStatusDef.createEmptyDelta(
            new ItemPath(UserType.F_ACTIVATION, ActivationType.F_EFFECTIVE_STATUS));
    effectiveStatusDelta.setValueToReplace(
        new PrismPropertyValue<ActivationStatusType>(
            effectiveStatusNew, OriginType.USER_POLICY, null));
    focusContext.swallowToProjectionWaveSecondaryDelta(effectiveStatusDelta);

    PropertyDelta<XMLGregorianCalendar> timestampDelta =
        LensUtil.createActivationTimestampDelta(
            effectiveStatusNew, now, activationDefinition, OriginType.USER_POLICY);
    focusContext.swallowToProjectionWaveSecondaryDelta(timestampDelta);
  }

  private PrismContainerDefinition<ActivationType> getActivationDefinition() {
    if (activationDefinition == null) {
      ComplexTypeDefinition focusDefinition =
          prismContext.getSchemaRegistry().findComplexTypeDefinition(FocusType.COMPLEX_TYPE);
      activationDefinition = focusDefinition.findContainerDefinition(FocusType.F_ACTIVATION);
    }
    return activationDefinition;
  }

  /** Adds deltas for iteration and iterationToken to the focus if needed. */
  private <F extends FocusType> void addIterationTokenDeltas(
      LensFocusContext<F> focusContext, int iteration, String iterationToken)
      throws SchemaException {
    PrismObject<F> objectCurrent = focusContext.getObjectCurrent();
    if (objectCurrent != null) {
      Integer iterationOld = objectCurrent.asObjectable().getIteration();
      String iterationTokenOld = objectCurrent.asObjectable().getIterationToken();
      if (iterationOld != null
          && iterationOld == iteration
          && iterationTokenOld != null
          && iterationTokenOld.equals(iterationToken)) {
        // Already stored
        return;
      }
    }
    PrismObjectDefinition<F> objDef = focusContext.getObjectDefinition();

    PrismPropertyValue<Integer> iterationVal = new PrismPropertyValue<Integer>(iteration);
    iterationVal.setOriginType(OriginType.USER_POLICY);
    PropertyDelta<Integer> iterationDelta =
        PropertyDelta.createReplaceDelta(objDef, FocusType.F_ITERATION, iterationVal);
    focusContext.swallowToSecondaryDelta(iterationDelta);

    PrismPropertyValue<String> iterationTokenVal = new PrismPropertyValue<String>(iterationToken);
    iterationTokenVal.setOriginType(OriginType.USER_POLICY);
    PropertyDelta<String> iterationTokenDelta =
        PropertyDelta.createReplaceDelta(objDef, FocusType.F_ITERATION_TOKEN, iterationTokenVal);
    focusContext.swallowToSecondaryDelta(iterationTokenDelta);
  }
}
Example #25
0
/**
 * @author lazyman
 * @author mserbak
 */
@PageDescriptor(
    url = "/admin/config/import",
    action = {
      @AuthorizationAction(
          actionUri = PageAdminConfiguration.AUTH_CONFIGURATION_ALL,
          label = PageAdminConfiguration.AUTH_CONFIGURATION_ALL_LABEL,
          description = PageAdminConfiguration.AUTH_CONFIGURATION_ALL_DESCRIPTION),
      @AuthorizationAction(
          actionUri = AuthorizationConstants.AUTZ_UI_CONFIGURATION_IMPORT_URL,
          label = "PageImportObject.auth.configImport.label",
          description = "PageImportObject.auth.configImport.description")
    })
public class PageImportObject extends PageAdminConfiguration {

  private static final Trace LOGGER = TraceManager.getTrace(PageImportObject.class);
  private static final String DOT_CLASS = PageImportObject.class.getName() + ".";
  private static final String OPERATION_IMPORT_FILE = DOT_CLASS + "importFile";
  private static final String OPERATION_IMPORT_XML = DOT_CLASS + "importXml";

  private static final String ID_MAIN_FORM = "mainForm";
  private static final String ID_BUTTON_BAR = "buttonBar";
  private static final String ID_IMPORT_OPTIONS = "importOptions";
  private static final String ID_IMPORT_RADIO_GROUP = "importRadioGroup";
  private static final String ID_FILE_RADIO = "fileRadio";
  private static final String ID_XML_RADIO = "xmlRadio";
  private static final String ID_BACK_BUTTON = "back";
  private static final String ID_IMPORT_FILE_BUTTON = "importFileButton";
  private static final String ID_IMPORT_XML_BUTTON = "importXmlButton";
  private static final String ID_INPUT = "input";
  private static final String ID_INPUT_ACE = "inputAce";
  private static final String ID_ACE_EDITOR = "aceEditor";
  private static final String ID_INPUT_FILE_LABEL = "inputFileLabel";
  private static final String ID_INPUT_FILE = "inputFile";
  private static final String ID_FILE_INPUT = "fileInput";

  public static final String FROM_MENU_ITEM_PARAM = "openedFromMenuItem";
  public static final String FROM_MENU_ITEM_PARAM_TRUE_VALUE = "1";
  public static final String FROM_MENU_ITEM_PARAM_FALSE_VALUE = "0";

  private static final Integer INPUT_FILE = 1;
  private static final Integer INPUT_XML = 2;

  private LoadableModel<ImportOptionsType> model;
  private IModel<String> xmlEditorModel;

  public PageImportObject() {
    model =
        new LoadableModel<ImportOptionsType>(false) {

          @Override
          protected ImportOptionsType load() {
            return MiscSchemaUtil.getDefaultImportOptions();
          }
        };
    xmlEditorModel = new Model<String>(null);

    initLayout();
  }

  private void initLayout() {
    Form mainForm = new Form(ID_MAIN_FORM);
    add(mainForm);

    ImportOptionsPanel importOptions = new ImportOptionsPanel(ID_IMPORT_OPTIONS, model);
    mainForm.add(importOptions);

    final WebMarkupContainer input = new WebMarkupContainer(ID_INPUT);
    input.setOutputMarkupId(true);
    mainForm.add(input);

    final WebMarkupContainer buttonBar = new WebMarkupContainer(ID_BUTTON_BAR);
    buttonBar.setOutputMarkupId(true);
    mainForm.add(buttonBar);

    final IModel<Integer> groupModel = new Model<Integer>(INPUT_FILE);
    RadioGroup importRadioGroup = new RadioGroup(ID_IMPORT_RADIO_GROUP, groupModel);
    importRadioGroup.add(
        new AjaxFormChoiceComponentUpdatingBehavior() {

          @Override
          protected void onUpdate(AjaxRequestTarget target) {
            target.add(input);
            target.add(buttonBar);
          }
        });
    mainForm.add(importRadioGroup);

    Radio fileRadio = new Radio(ID_FILE_RADIO, new Model(INPUT_FILE), importRadioGroup);
    importRadioGroup.add(fileRadio);

    Radio xmlRadio = new Radio(ID_XML_RADIO, new Model(INPUT_XML), importRadioGroup);
    importRadioGroup.add(xmlRadio);

    WebMarkupContainer inputAce = new WebMarkupContainer(ID_INPUT_ACE);
    addVisibileForInputType(inputAce, INPUT_XML, groupModel);
    input.add(inputAce);

    AceEditor aceEditor = new AceEditor(ID_ACE_EDITOR, xmlEditorModel);
    aceEditor.setOutputMarkupId(true);
    inputAce.add(aceEditor);

    WebMarkupContainer inputFileLabel = new WebMarkupContainer(ID_INPUT_FILE_LABEL);
    addVisibileForInputType(inputFileLabel, INPUT_FILE, groupModel);
    input.add(inputFileLabel);

    WebMarkupContainer inputFile = new WebMarkupContainer(ID_INPUT_FILE);
    addVisibileForInputType(inputFile, INPUT_FILE, groupModel);
    input.add(inputFile);

    FileUploadField fileInput = new FileUploadField(ID_FILE_INPUT);
    inputFile.add(fileInput);

    initButtons(buttonBar, groupModel);
  }

  private void addVisibileForInputType(
      Component comp, final Integer type, final IModel<Integer> groupModel) {
    comp.add(
        new VisibleEnableBehaviour() {

          @Override
          public boolean isVisible() {
            return type.equals(groupModel.getObject());
          }
        });
  }

  private void initButtons(WebMarkupContainer buttonBar, IModel<Integer> inputType) {
    AjaxButton backButton =
        new AjaxButton(ID_BACK_BUTTON, createStringResource("PageCertCampaign.button.back")) {

          @Override
          public void onClick(AjaxRequestTarget target) {
            redirectBack();
          }
        };
    backButton.add(
        new VisibleEnableBehaviour() {
          public boolean isVisible() {
            PageParameters params = PageImportObject.this.getPageParameters();
            if (params != null) {
              if (params.get(FROM_MENU_ITEM_PARAM) != null
                  && !params.get(FROM_MENU_ITEM_PARAM).isNull()
                  && params
                      .get(FROM_MENU_ITEM_PARAM)
                      .toString()
                      .equals(FROM_MENU_ITEM_PARAM_TRUE_VALUE)) {
                return false;
              }
            }
            return true;
          }
        });
    buttonBar.add(backButton);

    AjaxSubmitButton saveFileButton =
        new AjaxSubmitButton(
            ID_IMPORT_FILE_BUTTON, createStringResource("PageImportObject.button.import")) {

          @Override
          protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
            savePerformed(false, OPERATION_IMPORT_FILE, target);
          }

          @Override
          protected void onError(AjaxRequestTarget target, Form<?> form) {
            target.add(getFeedbackPanel());
          }
        };
    addVisibileForInputType(saveFileButton, INPUT_FILE, inputType);
    buttonBar.add(saveFileButton);

    AjaxSubmitButton saveXmlButton =
        new AjaxSubmitButton(
            ID_IMPORT_XML_BUTTON, createStringResource("PageImportObject.button.import")) {

          @Override
          protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
            savePerformed(true, OPERATION_IMPORT_XML, target);
          }

          @Override
          protected void onError(AjaxRequestTarget target, Form<?> form) {
            target.add(getFeedbackPanel());
          }
        };
    addVisibileForInputType(saveXmlButton, INPUT_XML, inputType);
    buttonBar.add(saveXmlButton);
  }

  private FileUpload getUploadedFile() {
    FileUploadField file =
        (FileUploadField)
            get(createComponentPath(ID_MAIN_FORM, ID_INPUT, ID_INPUT_FILE, ID_FILE_INPUT));
    return file.getFileUpload();
  }

  private boolean validateInput(boolean raw) {
    if (raw) {
      return StringUtils.isNotEmpty(xmlEditorModel.getObject());
    }
    return getUploadedFile() != null;
  }

  private InputStream getInputStream(boolean raw) throws Exception {
    if (raw) {
      return IOUtils.toInputStream(xmlEditorModel.getObject(), "utf-8");
    }
    File newFile = null;
    try {
      // Create new file
      MidPointApplication application = getMidpointApplication();
      WebApplicationConfiguration config = application.getWebApplicationConfiguration();
      File folder = new File(config.getImportFolder());
      if (!folder.exists() || !folder.isDirectory()) {
        folder.mkdir();
      }

      FileUpload uploadedFile = getUploadedFile();
      newFile = new File(folder, uploadedFile.getClientFileName());
      // Check new file, delete if it already exists
      if (newFile.exists()) {
        newFile.delete();
      }
      // Save file

      newFile.createNewFile();
      uploadedFile.writeTo(newFile);

      InputStreamReader reader = new InputStreamReader(new FileInputStream(newFile), "utf-8");
      return new ReaderInputStream(reader, reader.getEncoding());
    } finally {
      if (newFile != null) {
        FileUtils.deleteQuietly(newFile);
      }
    }
  }

  private void clearOldFeedback() {
    getSession().getFeedbackMessages().clear();
    getFeedbackMessages().clear();
  }

  private void savePerformed(boolean raw, String operationName, AjaxRequestTarget target) {
    clearOldFeedback();

    OperationResult result = new OperationResult(operationName);

    if (!validateInput(raw)) {
      error(getString("pageImportObject.message.nullFile"));
      target.add(getFeedbackPanel());

      return;
    }
    InputStream stream = null;

    try {
      Task task = createSimpleTask(operationName);
      stream = getInputStream(raw);
      getModelService().importObjectsFromStream(stream, model.getObject(), task, result);

      result.recomputeStatus();
    } catch (Exception ex) {
      result.recordFatalError("Couldn't import file.", ex);
      LoggingUtils.logUnexpectedException(LOGGER, "Couldn't import file", ex);
    } finally {
      if (stream != null) {
        IOUtils.closeQuietly(stream);
      }
    }

    showResult(result);
    target.add(PageImportObject.this);
  }
}
Example #26
0
/** @author lazyman */
@PageDescriptor(
    url = {"/admin/dashboard", "/admin"},
    action = {
      @AuthorizationAction(
          actionUri = PageAdminHome.AUTH_HOME_ALL_URI,
          label = PageAdminHome.AUTH_HOME_ALL_LABEL,
          description = PageAdminHome.AUTH_HOME_ALL_DESCRIPTION),
      @AuthorizationAction(
          actionUri = AuthorizationConstants.AUTZ_UI_DASHBOARD_URL,
          label = "PageDashboard.auth.dashboard.label",
          description = "PageDashboard.auth.dashboard.description")
    })
public class PageDashboard extends PageAdminHome {

  private static final Trace LOGGER = TraceManager.getTrace(PageDashboard.class);

  private static final String DOT_CLASS = PageDashboard.class.getName() + ".";
  private static final String OPERATION_LOAD_ACCOUNTS = DOT_CLASS + "loadAccounts";
  private static final String OPERATION_LOAD_ASSIGNMENTS = DOT_CLASS + "loadAssignments";

  private static final String ID_PERSONAL_INFO = "personalInfo";
  private static final String ID_ACCOUNTS = "accounts";
  private static final String ID_ASSIGNMENTS = "assignments";
  private static final String ID_SYSTEM_INFO = "systemInfo";

  private final Model<PrismObject<UserType>> principalModel = new Model<PrismObject<UserType>>();

  public PageDashboard() {
    principalModel.setObject(loadUserSelf(PageDashboard.this));
    initLayout();
  }

  private void initLayout() {
    initPersonalInfo();
    initMyAccounts();
    initAssignments();
    initSystemInfo();
  }

  private AccountCallableResult<List<SimpleAccountDto>> loadAccounts() throws Exception {
    LOGGER.debug("Loading accounts.");

    AccountCallableResult callableResult = new AccountCallableResult();
    List<SimpleAccountDto> list = new ArrayList<SimpleAccountDto>();
    callableResult.setValue(list);
    PrismObject<UserType> user = principalModel.getObject();
    if (user == null) {
      return callableResult;
    }

    Task task = createSimpleTask(OPERATION_LOAD_ACCOUNTS);
    OperationResult result = task.getResult();
    callableResult.setResult(result);
    Collection<SelectorOptions<GetOperationOptions>> options =
        SelectorOptions.createCollection(
            ShadowType.F_RESOURCE, GetOperationOptions.createResolve());

    List<ObjectReferenceType> references = user.asObjectable().getLinkRef();
    for (ObjectReferenceType reference : references) {
      PrismObject<ShadowType> account =
          WebModelUtils.loadObject(
              ShadowType.class, reference.getOid(), options, this, task, result);
      if (account == null) {
        continue;
      }

      ShadowType accountType = account.asObjectable();

      OperationResultType fetchResult = accountType.getFetchResult();

      if (fetchResult != null) {
        callableResult.getFetchResults().add(OperationResult.createOperationResult(fetchResult));
      }

      ResourceType resource = accountType.getResource();
      String resourceName = WebMiscUtil.getName(resource);
      list.add(
          new SimpleAccountDto(
              WebMiscUtil.getOrigStringFromPoly(accountType.getName()), resourceName));
    }
    result.recordSuccessIfUnknown();
    result.recomputeStatus();

    LOGGER.debug("Finished accounts loading.");

    return callableResult;
  }

  private void initPersonalInfo() {
    DashboardPanel personalInfo =
        new DashboardPanel(
            ID_PERSONAL_INFO,
            null,
            createStringResource("PageDashboard.personalInfo"),
            "fa fa-fw fa-male",
            DashboardColor.GRAY) {

          @Override
          protected Component getMainComponent(String componentId) {
            return new PersonalInfoPanel(componentId);
          }
        };
    add(personalInfo);
  }

  private void initSystemInfo() {
    DashboardPanel systemInfo =
        new DashboardPanel(
            ID_SYSTEM_INFO,
            null,
            createStringResource("PageDashboard.systemInfo"),
            "fa fa-tachometer",
            DashboardColor.GREEN) {

          @Override
          protected Component getMainComponent(String componentId) {
            return new SystemInfoPanel(componentId);
          }
        };
    add(systemInfo);
  }

  private void initMyAccounts() {
    AsyncDashboardPanel<Object, List<SimpleAccountDto>> accounts =
        new AsyncDashboardPanel<Object, List<SimpleAccountDto>>(
            ID_ACCOUNTS,
            createStringResource("PageDashboard.accounts"),
            "fa fa-fw fa-external-link",
            DashboardColor.BLUE) {

          @Override
          protected SecurityContextAwareCallable<CallableResult<List<SimpleAccountDto>>>
              createCallable(Authentication auth, IModel<Object> callableParameterModel) {

            return new SecurityContextAwareCallable<CallableResult<List<SimpleAccountDto>>>(
                getSecurityEnforcer(), auth) {

              @Override
              public AccountCallableResult<List<SimpleAccountDto>> callWithContextPrepared()
                  throws Exception {
                return loadAccounts();
              }
            };
          }

          @Override
          protected Component getMainComponent(String markupId) {
            return new MyAccountsPanel(
                markupId,
                new PropertyModel<List<SimpleAccountDto>>(getModel(), CallableResult.F_VALUE));
          }

          @Override
          protected void onPostSuccess(AjaxRequestTarget target) {
            showFetchResult();
            super.onPostSuccess(target);
          }

          @Override
          protected void onUpdateError(AjaxRequestTarget target, Exception ex) {
            showFetchResult();
            super.onUpdateError(target, ex);
          }

          private void showFetchResult() {
            AccountCallableResult<List<SimpleAccountDto>> result =
                (AccountCallableResult<List<SimpleAccountDto>>) getModel().getObject();

            PageBase page = (PageBase) getPage();
            for (OperationResult res : result.getFetchResults()) {
              if (!WebMiscUtil.isSuccessOrHandledError(res)) {
                page.showResult(res);
              }
            }
          }
        };
    add(accounts);
  }

  private void initAssignments() {
    AsyncDashboardPanel<Object, List<AssignmentItemDto>> assignedOrgUnits =
        new AsyncDashboardPanel<Object, List<AssignmentItemDto>>(
            ID_ASSIGNMENTS,
            createStringResource("PageDashboard.assignments"),
            "fa fa-fw fa-star",
            DashboardColor.YELLOW) {

          @Override
          protected SecurityContextAwareCallable<CallableResult<List<AssignmentItemDto>>>
              createCallable(Authentication auth, IModel callableParameterModel) {

            return new SecurityContextAwareCallable<CallableResult<List<AssignmentItemDto>>>(
                getSecurityEnforcer(), auth) {

              @Override
              public CallableResult<List<AssignmentItemDto>> callWithContextPrepared()
                  throws Exception {
                return loadAssignments();
              }
            };
          }

          @Override
          protected Component getMainComponent(String markupId) {
            return new MyAssignmentsPanel(
                markupId,
                new PropertyModel<List<AssignmentItemDto>>(getModel(), CallableResult.F_VALUE));
          }
        };
    add(assignedOrgUnits);
  }

  private CallableResult<List<AssignmentItemDto>> loadAssignments() throws Exception {
    LOGGER.debug("Loading assignments.");
    CallableResult callableResult = new CallableResult();
    List<AssignmentItemDto> list = new ArrayList<AssignmentItemDto>();
    callableResult.setValue(list);

    PrismObject<UserType> user = principalModel.getObject();
    if (user == null || user.findContainer(UserType.F_ASSIGNMENT) == null) {
      return callableResult;
    }

    Task task = createSimpleTask(OPERATION_LOAD_ASSIGNMENTS);
    OperationResult result = task.getResult();
    callableResult.setResult(result);

    PrismContainer assignments = user.findContainer(UserType.F_ASSIGNMENT);
    List<PrismContainerValue> values = assignments.getValues();
    for (PrismContainerValue assignment : values) {
      AssignmentItemDto item = createAssignmentItem(user, assignment, task, result);
      if (item != null) {
        list.add(item);
      }
    }
    result.recordSuccessIfUnknown();
    result.recomputeStatus();

    Collections.sort(list);

    LOGGER.debug("Finished assignments loading.");

    return callableResult;
  }

  private AssignmentItemDto createAssignmentItem(
      PrismObject<UserType> user,
      PrismContainerValue assignment,
      Task task,
      OperationResult result) {
    PrismReference targetRef = assignment.findReference(AssignmentType.F_TARGET_REF);
    if (targetRef == null || targetRef.isEmpty()) {
      // account construction
      PrismContainer construction = assignment.findContainer(AssignmentType.F_CONSTRUCTION);
      String name = null;
      String description = null;
      if (construction.getValue().asContainerable() != null && !construction.isEmpty()) {
        ConstructionType constr = (ConstructionType) construction.getValue().asContainerable();
        description =
            (String)
                construction.getPropertyRealValue(ConstructionType.F_DESCRIPTION, String.class);

        if (constr.getResourceRef() != null) {
          ObjectReferenceType resourceRef = constr.getResourceRef();

          PrismObject resource =
              WebModelUtils.loadObject(
                  ResourceType.class, resourceRef.getOid(), this, task, result);
          name = WebMiscUtil.getName(resource);
        }
      }

      return new AssignmentItemDto(
          AssignmentEditorDtoType.ACCOUNT_CONSTRUCTION, name, description, null);
    }

    PrismReferenceValue refValue = targetRef.getValue();
    PrismObject value = refValue.getObject();
    if (value == null) {
      // resolve reference
      value = WebModelUtils.loadObject(ObjectType.class, refValue.getOid(), this, task, result);
    }

    if (value == null) {
      // we couldn't resolve assignment details
      return new AssignmentItemDto(null, null, null, null);
    }

    String name = WebMiscUtil.getName(value);
    AssignmentEditorDtoType type = AssignmentEditorDtoType.getType(value.getCompileTimeClass());
    String relation = refValue.getRelation() != null ? refValue.getRelation().getLocalPart() : null;
    String description = null;
    if (RoleType.class.isAssignableFrom(value.getCompileTimeClass())) {
      description = (String) value.getPropertyRealValue(RoleType.F_DESCRIPTION, String.class);
    }

    return new AssignmentItemDto(type, name, description, relation);
  }

  @Override
  public PageBase reinitialize() {
    return new PageDashboard();
  }
}
Example #27
0
/** @author mserbak */
@PageDescriptor(url = "/login")
public class PageLogin extends PageBase {
  private static final long serialVersionUID = 1L;

  private static final Trace LOGGER = TraceManager.getTrace(PageLogin.class);

  private static final String ID_FORGET_PASSWORD = "******";
  private static final String ID_SELF_REGISTRATION = "selfRegistration";

  private static final String DOT_CLASS = PageLogin.class.getName() + ".";
  protected static final String OPERATION_LOAD_RESET_PASSWORD_POLICY =
      DOT_CLASS + "loadPasswordResetPolicy";
  private static final String OPERATION_LOAD_REGISTRATION_POLICY =
      DOT_CLASS + "loadRegistrationPolicy";

  public PageLogin() {
    if (SecurityUtils.getPrincipalUser() != null) {
      MidPointApplication app = getMidpointApplication();
      setResponsePage(app.getHomePage());
    }

    BookmarkablePageLink<String> link =
        new BookmarkablePageLink<>(ID_FORGET_PASSWORD, PageForgotPassword.class);
    link.add(
        new VisibleEnableBehaviour() {
          private static final long serialVersionUID = 1L;

          @Override
          public boolean isVisible() {
            OperationResult parentResult =
                new OperationResult(OPERATION_LOAD_RESET_PASSWORD_POLICY);

            SecurityPolicyType securityPolicy = null;
            try {
              securityPolicy =
                  getModelInteractionService().getSecurityPolicy(null, null, parentResult);
            } catch (ObjectNotFoundException | SchemaException e) {
              LOGGER.warn("Cannot read credentials policy: " + e.getMessage(), e);
            }

            boolean linkIsVisible = false;

            if (securityPolicy == null) {
              return linkIsVisible;
            }

            CredentialsPolicyType creds = securityPolicy.getCredentials();

            if (creds != null
                && ((creds.getSecurityQuestions() != null
                        && creds.getSecurityQuestions().getQuestionNumber() != null)
                    || (securityPolicy.getCredentialsReset() != null))) {
              linkIsVisible = true;
            }

            return linkIsVisible;
          }
        });
    add(link);

    AjaxLink<String> registration =
        new AjaxLink<String>(ID_SELF_REGISTRATION) {

          @Override
          public void onClick(AjaxRequestTarget target) {
            setResponsePage(PageSelfRegistration.class);
          }
        };
    registration.add(
        new VisibleEnableBehaviour() {
          private static final long serialVersionUID = 1L;

          @Override
          public boolean isVisible() {
            OperationResult parentResult = new OperationResult(OPERATION_LOAD_REGISTRATION_POLICY);

            RegistrationsPolicyType registrationPolicies = null;
            try {
              Task task = createAnonymousTask(OPERATION_LOAD_REGISTRATION_POLICY);
              registrationPolicies =
                  getModelInteractionService().getRegistrationPolicy(null, task, parentResult);
            } catch (ObjectNotFoundException | SchemaException e) {
              LOGGER.warn("Cannot read credentials policy: " + e.getMessage(), e);
            }

            boolean linkIsVisible = false;
            if (registrationPolicies != null
                && registrationPolicies.getSelfRegistration() != null) {
              linkIsVisible = true;
            }

            return linkIsVisible;
          }
        });
    add(registration);
  }

  @Override
  protected void onConfigure() {
    super.onConfigure();

    ServletWebRequest req = (ServletWebRequest) RequestCycle.get().getRequest();
    HttpServletRequest httpReq = req.getContainerRequest();
    HttpSession httpSession = httpReq.getSession();

    Exception ex = (Exception) httpSession.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
    if (ex == null) {
      return;
    }

    String key = ex.getMessage() != null ? ex.getMessage() : "web.security.provider.unavailable";
    error(getString(key));

    httpSession.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);

    clearBreadcrumbs();
  }

  @Override
  protected void createBreadcrumb() {
    // don't create breadcrumb for login page
  }
}
Example #28
0
/** @author Kate Honchar */
public class LinksPanel extends SimplePanel<List<RichHyperlinkType>> {
  private static final String DOT_CLASS = LinksPanel.class.getName() + ".";
  private static final String ID_IMAGE = "imageId";
  private static final String ID_LINK = "link";
  private static final String ID_LABEL = "labelId";
  private static final String ID_DESCRIPTION = "descriptionId";
  private static final String ID_LINKS_ROW = "linksRow";
  private static final String ID_LINKS_COLUMN = "linksColumn";

  private static final String ICON_DEFAULT_CSS_CLASS = "fa fa-angle-double-right";

  private static final Trace LOGGER = TraceManager.getTrace(LinksPanel.class);

  IModel<List<RichHyperlinkType>> model;

  public LinksPanel(String id) {
    super(id, null);
  }

  public LinksPanel(
      String id, IModel<List<RichHyperlinkType>> model, final List<RichHyperlinkType> linksList) {
    super(id, model);
  }

  @Override
  protected void initLayout() {

    final List<RichHyperlinkType> linksList = getModel().getObject();
    RepeatingView rowView = new RepeatingView(ID_LINKS_ROW);

    int linksListSize = linksList == null ? 0 : linksList.size();
    if (linksListSize > 0) {
      int currentColumn = 0;
      RepeatingView columnView = null;
      WebMarkupContainer row = null;
      boolean isRowAdded = false;
      for (int i = 0; i < linksListSize; i++) {
        final RichHyperlinkType link = linksList.get(i);
        if (WebMiscUtil.isAuthorized(link.getAuthorization())) {
          if (currentColumn == 0) {
            row = new WebMarkupContainer(rowView.newChildId());
            isRowAdded = false;
            columnView = new RepeatingView(ID_LINKS_COLUMN);
          }
          WebMarkupContainer column = new WebMarkupContainer(columnView.newChildId());
          Link linkItem =
              new Link(ID_LINK) {
                @Override
                public void onClick() {}

                @Override
                protected void onComponentTag(final ComponentTag tag) {
                  super.onComponentTag(tag);
                  String rootContext = "";
                  if (link.getTargetUrl() != null
                      && !link.getTargetUrl().startsWith("http://")
                      && !link.getTargetUrl().startsWith("https://")
                      && !link.getTargetUrl().startsWith("www://")
                      && !link.getTargetUrl().startsWith("//")) {
                    WebApplication webApplication = WebApplication.get();
                    if (webApplication != null) {
                      ServletContext servletContext = webApplication.getServletContext();
                      if (servletContext != null) {
                        rootContext = servletContext.getContextPath();
                      }
                    }
                  }
                  tag.put("href", rootContext + link.getTargetUrl());
                }
              };
          linkItem.add(
              new Label(ID_IMAGE) {
                @Override
                protected void onComponentTag(final ComponentTag tag) {
                  super.onComponentTag(tag);
                  String cssClass = ICON_DEFAULT_CSS_CLASS;
                  if (link.getIcon() != null) {
                    cssClass = link.getIcon().getCssClass();
                  }
                  tag.put(
                      "class",
                      "info-box-icon "
                          + (link.getColor() != null
                              ? (link.getColor().startsWith("bg-")
                                  ? link.getColor()
                                  : "bg-" + link.getColor())
                              : "")
                          + " "
                          + cssClass);
                }
              });

          linkItem.add(
              new Label(
                  ID_LABEL,
                  new Model<String>() {
                    public String getObject() {
                      return link.getLabel();
                    }
                  }));
          Label description =
              new Label(
                  ID_DESCRIPTION,
                  new Model<String>() {
                    public String getObject() {
                      return link.getDescription();
                    }
                  });
          description.setEnabled(false);
          linkItem.add(description);

          column.add(linkItem);
          columnView.add(column);
          if (currentColumn == 1 || (linksList.indexOf(link) == linksListSize - 1)) {
            row.add(columnView);
            rowView.add(row);
            currentColumn = 0;
            isRowAdded = true;
          } else {
            currentColumn++;
          }
        } else {
          LOGGER.trace("Link {} not authorized, skipping", link);
        }
      }
      if (row != null && columnView != null && !isRowAdded) {
        row.add(columnView);
        rowView.add(row);
      }
    }
    add(rowView);
  }
}
/** @author lazyman */
@Component("midpointApplication")
public class MidPointApplication extends AuthenticatedWebApplication {

  /** Max. photo size for user/jpegPhoto */
  public static final Bytes FOCUS_PHOTO_MAX_FILE_SIZE = Bytes.kilobytes(192);

  public static final String WEB_APP_CONFIGURATION = "midpoint.webApplication";

  public static final List<LocaleDescriptor> AVAILABLE_LOCALES;

  private static final String LOCALIZATION_DESCRIPTOR = "/localization/locale.properties";

  private static final String PROP_NAME = ".name";
  private static final String PROP_FLAG = ".flag";
  private static final String PROP_DEFAULT = ".default";

  private static final Trace LOGGER = TraceManager.getTrace(MidPointApplication.class);

  static {
    List<LocaleDescriptor> locales = new ArrayList<>();
    try {
      ClassLoader classLoader = MidPointApplication.class.getClassLoader();
      Enumeration<URL> urls = classLoader.getResources(LOCALIZATION_DESCRIPTOR);
      while (urls.hasMoreElements()) {
        final URL url = urls.nextElement();
        LOGGER.debug("Found localization descriptor {}.", new Object[] {url.toString()});

        Properties properties = new Properties();
        Reader reader = null;
        try {
          reader = new InputStreamReader(url.openStream(), "utf-8");
          properties.load(reader);

          Map<String, Map<String, String>> localeMap = new HashMap<>();
          Set<String> keys = (Set) properties.keySet();
          for (String key : keys) {
            String[] array = key.split("\\.");
            if (array.length != 2) {
              continue;
            }

            String locale = array[0];
            Map<String, String> map = localeMap.get(locale);
            if (map == null) {
              map = new HashMap<>();
              localeMap.put(locale, map);
            }

            map.put(key, properties.getProperty(key));
          }

          for (String key : localeMap.keySet()) {
            Map<String, String> localeDefinition = localeMap.get(key);
            if (!localeDefinition.containsKey(key + PROP_NAME)
                || !localeDefinition.containsKey(key + PROP_FLAG)) {
              continue;
            }

            LocaleDescriptor descriptor =
                new LocaleDescriptor(
                    localeDefinition.get(key + PROP_NAME),
                    localeDefinition.get(key + PROP_FLAG),
                    localeDefinition.get(key + PROP_DEFAULT),
                    WebComponentUtil.getLocaleFromString(key));
            locales.add(descriptor);
          }
        } catch (Exception ex) {
          LoggingUtils.logUnexpectedException(LOGGER, "Couldn't load localization", ex);
        } finally {
          IOUtils.closeQuietly(reader);
        }
      }

      Collections.sort(locales);
    } catch (Exception ex) {
      LoggingUtils.logUnexpectedException(LOGGER, "Couldn't load locales", ex);
    }

    AVAILABLE_LOCALES = Collections.unmodifiableList(locales);
  }

  @Autowired transient ModelService model;
  @Autowired transient ModelInteractionService modelInteractionService;
  @Autowired transient TaskService taskService;
  @Autowired transient PrismContext prismContext;
  @Autowired transient ExpressionFactory expressionFactory;
  @Autowired transient TaskManager taskManager;
  @Autowired transient ModelAuditService auditService;
  @Autowired private transient RepositoryService repositoryService; // temporary
  @Autowired private transient WorkflowService workflowService;
  @Autowired private transient WorkflowManager workflowManager;
  @Autowired transient MidpointConfiguration configuration;
  @Autowired transient Protector protector;
  @Autowired transient MatchingRuleRegistry matchingRuleRegistry;
  @Autowired transient SecurityEnforcer securityEnforcer;

  private WebApplicationConfiguration webApplicationConfiguration;

  @Override
  protected void onDestroy() {
    GuiComponents.destroy();

    super.onDestroy();
  }

  @Override
  public Class<? extends PageBase> getHomePage() {
    if (WebComponentUtil.isAuthorized(
        AuthorizationConstants.AUTZ_UI_DASHBOARD_URL,
        AuthorizationConstants.AUTZ_UI_HOME_ALL_URL)) {
      return PageDashboard.class;
    } else {
      return PageSelfDashboard.class;
    }
  }

  @Override
  public void init() {
    super.init();

    getFrameworkSettings().setSerializer(new Fast2WicketSerializer());

    getJavaScriptLibrarySettings()
        .setJQueryReference(
            new PackageResourceReference(
                MidPointApplication.class,
                "../../../../../webjars/adminlte/2.3.0/plugins/jQuery/jQuery-2.1.4.min.js"));

    GuiComponents.init();

    getComponentInstantiationListeners().add(new SpringComponentInjector(this));

    ResourceSettings resourceSettings = getResourceSettings();
    resourceSettings.setParentFolderPlaceholder("$-$");
    resourceSettings.setHeaderItemComparator(new PriorityFirstComparator(true));
    SecurePackageResourceGuard guard =
        (SecurePackageResourceGuard) resourceSettings.getPackageResourceGuard();
    guard.addPattern("+*.woff2");

    List<IStringResourceLoader> resourceLoaders = resourceSettings.getStringResourceLoaders();
    resourceLoaders.add(0, new Utf8BundleStringResourceLoader("localization/Midpoint"));
    resourceLoaders.add(
        1,
        new Utf8BundleStringResourceLoader(
            SchemaConstants.SCHEMA_LOCALIZATION_PROPERTIES_RESOURCE_BASE_PATH));

    resourceSettings.setThrowExceptionOnMissingResource(false);
    getMarkupSettings().setStripWicketTags(true);
    //        getMarkupSettings().setDefaultBeforeDisabledLink("");
    //        getMarkupSettings().setDefaultAfterDisabledLink("");

    if (RuntimeConfigurationType.DEVELOPMENT.equals(getConfigurationType())) {
      getDebugSettings().setAjaxDebugModeEnabled(true);
      getDebugSettings().setDevelopmentUtilitiesEnabled(true);
    }

    // pretty url for resources (e.g. images)
    mountFiles(ImgResources.BASE_PATH, ImgResources.class);

    // exception handling an error pages
    ApplicationSettings appSettings = getApplicationSettings();
    appSettings.setAccessDeniedPage(PageError401.class);
    appSettings.setInternalErrorPage(PageError.class);
    appSettings.setPageExpiredErrorPage(PageError.class);

    mount(new MountedMapper("/error", PageError.class, MidPointPageParametersEncoder.ENCODER));
    mount(
        new MountedMapper("/error/401", PageError401.class, MidPointPageParametersEncoder.ENCODER));
    mount(
        new MountedMapper("/error/403", PageError403.class, MidPointPageParametersEncoder.ENCODER));
    mount(
        new MountedMapper("/error/404", PageError404.class, MidPointPageParametersEncoder.ENCODER));
    mount(
        new MountedMapper("/error/410", PageError410.class, MidPointPageParametersEncoder.ENCODER));

    getRequestCycleListeners().add(new LoggingRequestCycleListener(this));

    // descriptor loader, used for customization
    new DescriptorLoader().loadData(this);
  }

  private void mountFiles(String path, Class<?> clazz) {
    try {
      List<Resource> list = new ArrayList<>();
      String packagePath = clazz.getPackage().getName().replace('.', '/');

      PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
      Resource[] res = resolver.getResources("classpath:" + packagePath + "/*.png");
      if (res != null) {
        list.addAll(Arrays.asList(res));
      }
      res = resolver.getResources("classpath:" + packagePath + "/*.gif");
      if (res != null) {
        list.addAll(Arrays.asList(res));
      }

      for (Resource resource : list) {
        URI uri = resource.getURI();
        File file = new File(uri.toString());
        mountResource(
            path + "/" + file.getName(), new SharedResourceReference(clazz, file.getName()));
      }
    } catch (Exception ex) {
      LoggingUtils.logUnexpectedException(LOGGER, "Couldn't mount files", ex);
    }
  }

  public WebApplicationConfiguration getWebApplicationConfiguration() {
    if (webApplicationConfiguration == null) {
      Configuration config = configuration.getConfiguration(WEB_APP_CONFIGURATION);
      webApplicationConfiguration = new WebApplicationConfiguration(config);
    }
    return webApplicationConfiguration;
  }

  public SecurityEnforcer getSecurityEnforcer() {
    return securityEnforcer;
  }

  public ModelService getModel() {
    return model;
  }

  public TaskManager getTaskManager() {
    return taskManager;
  }

  public ModelAuditService getAuditService() {
    return auditService;
  }

  public RepositoryService getRepositoryService() {
    return repositoryService;
  }

  public TaskService getTaskService() {
    return taskService;
  }

  public PrismContext getPrismContext() {
    return prismContext;
  }

  public ExpressionFactory getExpressionFactory() {
    return expressionFactory;
  }

  public Protector getProtector() {
    return protector;
  }

  @Override
  protected Class<? extends WebPage> getSignInPageClass() {
    return PageLogin.class;
  }

  @Override
  protected Class<? extends AbstractAuthenticatedWebSession> getWebSessionClass() {
    return MidPointAuthWebSession.class;
  }

  public WorkflowService getWorkflowService() {
    return workflowService;
  }

  public WorkflowManager getWorkflowManager() {
    return workflowManager;
  }

  public ModelInteractionService getModelInteractionService() {
    return modelInteractionService;
  }

  public static boolean containsLocale(Locale locale) {
    if (locale == null) {
      return false;
    }

    for (LocaleDescriptor descriptor : AVAILABLE_LOCALES) {
      if (locale.equals(descriptor.getLocale())) {
        return true;
      }
    }

    return false;
  }

  public static Locale getDefaultLocale() {
    for (LocaleDescriptor descriptor : AVAILABLE_LOCALES) {
      if (descriptor.isDefault()) {
        return descriptor.getLocale();
      }
    }

    return new Locale("en", "US");
  }

  public MatchingRuleRegistry getMatchingRuleRegistry() {
    return matchingRuleRegistry;
  }

  private static class ResourceFileFilter implements FilenameFilter {

    @Override
    public boolean accept(File parent, String name) {
      if (name.endsWith("png") || name.endsWith("gif")) {
        return true;
      }

      return false;
    }
  }
}
/**
 * Responsible for keeping the cluster consistent. (Clusterwide task management operations are in
 * ExecutionManager.)
 *
 * @author Pavol Mederly
 */
public class ClusterManager {

  private static final transient Trace LOGGER = TraceManager.getTrace(ClusterManager.class);

  private static final String CLASS_DOT = ClusterManager.class.getName() + ".";
  private static final String CHECK_SYSTEM_CONFIGURATION_CHANGED =
      CLASS_DOT + "checkSystemConfigurationChanged";
  private static final String CHECK_WAITING_TASKS = CLASS_DOT + "checkWaitingTasks";

  private TaskManagerQuartzImpl taskManager;

  private NodeRegistrar nodeRegistrar;

  private ClusterManagerThread clusterManagerThread;

  public ClusterManager(TaskManagerQuartzImpl taskManager) {
    this.taskManager = taskManager;
    this.nodeRegistrar = new NodeRegistrar(taskManager, this);
  }

  /**
   * Verifies cluster consistency (currently checks whether there is no other node with the same ID,
   * and whether clustered/non-clustered nodes are OK).
   *
   * @param result
   * @return
   */
  public void checkClusterConfiguration(OperationResult result) {

    //        LOGGER.trace("taskManager = " + taskManager);
    //        LOGGER.trace("taskManager.getNodeRegistrar() = " + taskManager.getNodeRegistrar());

    nodeRegistrar.verifyNodeObject(
        result); // if error, sets the error state and stops the scheduler
    nodeRegistrar.checkNonClusteredNodes(result); // the same
  }

  public boolean isClusterManagerThreadActive() {
    return clusterManagerThread != null && clusterManagerThread.isAlive();
  }

  public void recordNodeShutdown(OperationResult result) {
    nodeRegistrar.recordNodeShutdown(result);
  }

  public String getNodeId() {
    return nodeRegistrar.getNodeId();
  }

  public boolean isCurrentNode(PrismObject<NodeType> node) {
    return nodeRegistrar.isCurrentNode(node);
  }

  public boolean isCurrentNode(String node) {
    return nodeRegistrar.isCurrentNode(node);
  }

  public void deleteNode(String nodeOid, OperationResult result)
      throws SchemaException, ObjectNotFoundException {
    nodeRegistrar.deleteNode(nodeOid, result);
  }

  public void createNodeObject(OperationResult result) throws TaskManagerInitializationException {
    nodeRegistrar.createNodeObject(result);
  }

  public PrismObject<NodeType> getNodePrism() {
    return nodeRegistrar.getNodePrism();
  }

  public boolean isUp(NodeType nodeType) {
    return nodeRegistrar.isUp(nodeType);
  }

  class ClusterManagerThread extends Thread {

    boolean canRun = true;

    @Override
    public void run() {
      LOGGER.info("ClusterManager thread starting.");

      long delay = taskManager.getConfiguration().getNodeRegistrationCycleTime() * 1000L;
      while (canRun) {

        OperationResult result = new OperationResult(ClusterManagerThread.class + ".run");

        try {

          checkSystemConfigurationChanged(result);

          // these checks are separate in order to prevent a failure in one method blocking
          // execution of others
          try {
            checkClusterConfiguration(result); // if error, the scheduler will be stopped
            nodeRegistrar.updateNodeObject(
                result); // however, we want to update repo even in that case
          } catch (Throwable t) {
            LoggingUtils.logException(
                LOGGER,
                "Unexpected exception while checking cluster configuration; continuing execution.",
                t);
          }

          try {
            checkWaitingTasks(result);
          } catch (Throwable t) {
            LoggingUtils.logException(
                LOGGER,
                "Unexpected exception while checking waiting tasks; continuing execution.",
                t);
          }

          try {
            checkStalledTasks(result);
          } catch (Throwable t) {
            LoggingUtils.logException(
                LOGGER,
                "Unexpected exception while checking stalled tasks; continuing execution.",
                t);
          }

        } catch (Throwable t) {
          LoggingUtils.logException(
              LOGGER, "Unexpected exception in ClusterManager thread; continuing execution.", t);
        }

        LOGGER.trace("ClusterManager thread sleeping for " + delay + " msec");
        try {
          Thread.sleep(delay);
        } catch (InterruptedException e) {
          LOGGER.trace("ClusterManager thread interrupted.");
        }
      }

      LOGGER.info("ClusterManager thread stopping.");
    }

    public void signalShutdown() {
      canRun = false;
      this.interrupt();
    }
  }

  public void stopClusterManagerThread(long waitTime, OperationResult parentResult) {

    OperationResult result =
        parentResult.createSubresult(ClusterManager.class.getName() + ".stopClusterManagerThread");
    result.addParam("waitTime", waitTime);

    if (clusterManagerThread != null) {
      clusterManagerThread.signalShutdown();
      try {
        clusterManagerThread.join(waitTime);
      } catch (InterruptedException e) {
        LoggingUtils.logException(
            LOGGER, "Waiting for ClusterManagerThread shutdown was interrupted", e);
      }
      if (clusterManagerThread.isAlive()) {
        result.recordWarning(
            "ClusterManagerThread shutdown requested but after "
                + waitTime
                + " ms it is still running.");
      } else {
        result.recordSuccess();
      }
    } else {
      result.recordSuccess();
    }
  }

  public void startClusterManagerThread() {
    clusterManagerThread = new ClusterManagerThread();
    clusterManagerThread.setName("ClusterManagerThread");
    clusterManagerThread.start();
  }

  private RepositoryService getRepositoryService() {
    return taskManager.getRepositoryService();
  }

  public String dumpNodeInfo(NodeType node) {
    return node.getNodeIdentifier() + " (" + node.getHostname() + ")";
  }

  private OperationResult createOperationResult(String methodName) {
    return new OperationResult(ClusterManager.class.getName() + "." + methodName);
  }

  public List<PrismObject<NodeType>> getAllNodes(OperationResult result) {
    try {
      return getRepositoryService().searchObjects(NodeType.class, null, null, result);
    } catch (SchemaException e) { // should not occur
      throw new SystemException("Cannot get the list of nodes from the repository", e);
    }
  }

  public PrismObject<NodeType> getNode(String nodeOid, OperationResult result)
      throws SchemaException, ObjectNotFoundException {
    return getRepositoryService().getObject(NodeType.class, nodeOid, null, result);
  }

  public PrismObject<NodeType> getNodeById(String nodeIdentifier, OperationResult result)
      throws ObjectNotFoundException {
    try {
      //            QueryType q = QueryUtil.createNameQuery(nodeIdentifier);        // TODO change
      // to query-by-node-id
      ObjectQuery q =
          ObjectQueryUtil.createNameQuery(
              NodeType.class, taskManager.getPrismContext(), nodeIdentifier);
      List<PrismObject<NodeType>> nodes =
          taskManager.getRepositoryService().searchObjects(NodeType.class, q, null, result);
      if (nodes.isEmpty()) {
        //                result.recordFatalError("A node with identifier " + nodeIdentifier + "
        // does not exist.");
        throw new ObjectNotFoundException(
            "A node with identifier " + nodeIdentifier + " does not exist.");
      } else if (nodes.size() > 1) {
        throw new SystemException(
            "Multiple nodes with the same identifier '" + nodeIdentifier + "' in the repository.");
      } else {
        return nodes.get(0);
      }
    } catch (SchemaException e) { // should not occur
      throw new SystemException("Cannot get the list of nodes from the repository", e);
    }
  }

  /**
   * Check whether system configuration has not changed in repository (e.g. by another node in
   * cluster). Applies new configuration if so.
   *
   * @param parentResult
   */
  public void checkSystemConfigurationChanged(OperationResult parentResult) {

    OperationResult result = parentResult.createSubresult(CHECK_SYSTEM_CONFIGURATION_CHANGED);

    PrismObject<SystemConfigurationType> systemConfiguration;
    try {
      PrismObject<SystemConfigurationType> config =
          getRepositoryService()
              .getObject(
                  SystemConfigurationType.class,
                  SystemObjectsType.SYSTEM_CONFIGURATION.value(),
                  null,
                  result);

      String versionInRepo = config.getVersion();
      String versionApplied = LoggingConfigurationManager.getCurrentlyUsedVersion();

      // we do not try to determine which one is "newer" - we simply use the one from repo
      if (!versionInRepo.equals(versionApplied)) {
        LoggingConfigurationType loggingConfig =
            ProfilingConfigurationManager.checkSystemProfilingConfiguration(config);
        LoggingConfigurationManager.configure(loggingConfig, versionInRepo, result);
      } else {
        if (LOGGER.isTraceEnabled()) {
          LOGGER.trace(
              "System configuration change check: version in repo = version currently applied = {}",
              versionApplied);
        }
      }

      if (result.isUnknown()) {
        result.computeStatus();
      }

    } catch (ObjectNotFoundException e) {
      LoggingConfigurationManager
          .resetCurrentlyUsedVersion(); // because the new config (if any) will have version number
                                        // probably starting at 1 - so to be sure to read it when it
                                        // comes [hope this never occurs :)]
      String message = "No system configuration found, skipping application of system settings";
      LOGGER.error(message + ": " + e.getMessage(), e);
      result.recordWarning(message, e);
    } catch (SchemaException e) {
      String message =
          "Schema error in system configuration, skipping application of system settings";
      LOGGER.error(message + ": " + e.getMessage(), e);
      result.recordWarning(message, e);
    } catch (RuntimeException e) {
      String message =
          "Runtime exception in system configuration processing, skipping application of system settings";
      LOGGER.error(message + ": " + e.getMessage(), e);
      result.recordWarning(message, e);
    }
  }

  private long lastCheckedWaitingTasks = 0L;

  public void checkWaitingTasks(OperationResult result) throws SchemaException {
    if (System.currentTimeMillis()
        > lastCheckedWaitingTasks
            + taskManager.getConfiguration().getWaitingTasksCheckInterval() * 1000L) {
      lastCheckedWaitingTasks = System.currentTimeMillis();
      taskManager.checkWaitingTasks(result);
    }
  }

  private long lastCheckedStalledTasks = 0L;

  public void checkStalledTasks(OperationResult result) throws SchemaException {
    if (System.currentTimeMillis()
        > lastCheckedStalledTasks
            + taskManager.getConfiguration().getStalledTasksCheckInterval() * 1000L) {
      lastCheckedStalledTasks = System.currentTimeMillis();
      taskManager.checkStalledTasks(result);
    }
  }
}