com.evolveum.midpoint.testing.model.client.sample.TestExchangeConnector.java Source code

Java tutorial

Introduction

Here is the source code for com.evolveum.midpoint.testing.model.client.sample.TestExchangeConnector.java

Source

/*
 * Copyright (c) 2010-2014 Evolveum
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.evolveum.midpoint.testing.model.client.sample;

import com.evolveum.midpoint.model.client.ModelClientUtil;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.GetOperationOptionsType;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectDeltaListType;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectDeltaOperationListType;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectListType;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectSelectorType;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.RetrieveOptionType;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.SelectorQualifiedGetOptionType;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.SelectorQualifiedGetOptionsType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ModelExecuteOptionsType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectDeltaOperationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultStatusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowAttributesType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemObjectsType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;
import com.evolveum.midpoint.xml.ns._public.common.fault_3.FaultMessage;
import com.evolveum.midpoint.xml.ns._public.model.model_3.ModelPortType;
import com.evolveum.midpoint.xml.ns._public.model.model_3.ModelService;
import com.evolveum.prism.xml.ns._public.query_3.OrderDirectionType;
import com.evolveum.prism.xml.ns._public.query_3.PagingType;
import com.evolveum.prism.xml.ns._public.query_3.QueryType;
import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType;
import com.evolveum.prism.xml.ns._public.types_3.ChangeTypeType;
import com.evolveum.prism.xml.ns._public.types_3.ItemDeltaType;
import com.evolveum.prism.xml.ns._public.types_3.ItemPathType;
import com.evolveum.prism.xml.ns._public.types_3.ModificationTypeType;
import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.Validate;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
import org.apache.wss4j.dom.WSConstants;
import org.apache.wss4j.dom.handler.WSHandlerConstants;
import org.testng.AssertJUnit;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Holder;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * An attempt to have an automated test for Exchange connector.
 *
 * To minimalize technical problems of communication with remote Windows machine
 * the commands to the connectors are sent via midPoint (Web Service) API.
 *
 * To run this test the following is required:
 * (1) A Windows machine (anywhere in the network), with the following set up and running:
 *     a) Microsoft Exchange
 *     b) Connector Server and Exchange connector
 * (2) A midPoint machine (anywhere in the network), with the following elements configured:
 *     a) ConnectorHost pointing to the Windows machine
 *     b) discovered Exchange connector
 *     c) adequately defined Exchange resource (TODO specify more exactly)
 *
 * To run this test, the following system properties have to be provided:
 *
 *  - resourceOid (e.g. "11111111-2222-1111-1111-000000000010")
 *     -> the OID of Exchange resource (see 2c above)
 *  - container (e.g. "OU=ConnectorTest,DC=xxxx,DC=yyyy,DC=com")
 *     -> in which container the testing would take place
 *        (must correspond to the container defined in the Exchange resource)
 *  - mailDomain (e.g. "xxxx.yyyy.com")
 *     -> used for testing mail addresses for user accounts created by the connector
 *        (must correspond to address policies specified on the Exchange server)
 *
 * Again, this is HIGHLY EXPERIMENTAL.
 *
 * @author mederly
 *
 */
public class TestExchangeConnector {

    // Configuration
    public static final String ADM_USERNAME = "administrator";
    public static final String ADM_PASSWORD = "5ecr3t";
    private static final String DEFAULT_ENDPOINT_URL = "http://localhost.:8080/midpoint/model/model-3";

    // Object OIDs

    // Other
    private static final String NS_RI = "http://midpoint.evolveum.com/xml/ns/public/resource/instance-3";
    private static final String NS_ICFS = "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/resource-schema-3";

    private static final QName OC_ACCOUNT = new QName(NS_RI, "AccountObjectClass");
    private static final QName OC_ACCEPTED_DOMAIN = new QName(NS_RI, "CustomAcceptedDomainObjectClass");
    private static final QName OC_GLOBAL_ADDRESS_LIST = new QName(NS_RI, "CustomGlobalAddressListObjectClass");
    private static final QName OC_ADDRESS_LIST = new QName(NS_RI, "CustomAddressListObjectClass");
    private static final QName OC_OFFLINE_ADDRESS_BOOK = new QName(NS_RI, "CustomOfflineAddressBookObjectClass");
    private static final QName OC_ADDRESS_BOOK_POLICY = new QName(NS_RI, "CustomAddressBookPolicyObjectClass");
    private static final QName OC_DISTRIBUTION_GROUP = new QName(NS_RI, "CustomDistributionGroupObjectClass");
    private static final QName OC_EMAIL_ADDRESS_POLICY = new QName(NS_RI, "CustomEmailAddressPolicyObjectClass");

    private static final String NEWTON_GIVEN_NAME = "Isaac";
    private static final String NEWTON_SN = "Newton";
    private String newtonOid;

    private static final String LEIBNIZ_GIVEN_NAME = "Gottfried Wilhelm";
    private static final String LEIBNIZ_SN = "Leibniz";
    private String leibnizOid;

    private static final String PASCAL_GIVEN_NAME = "Blaise";
    private static final String PASCAL_SN = "Pascal";
    private String pascalOid;

    private List<String> acceptedDomains = new ArrayList<>();
    private List<String> globalAddressLists = new ArrayList<>();
    private List<String> addressLists = new ArrayList<>();
    private List<String> offlineAddressBooks = new ArrayList<>();
    private List<String> addressBookPolicies = new ArrayList<>();
    private List<String> distributionGroups = new ArrayList<>();
    private List<String> emailAddressPolicies = new ArrayList<>();

    private String dn(String givenName, String sn) {
        return "CN=" + givenName + " " + sn + "," + getContainer();
    }

    private String mail(String givenName, String sn) {
        return sn.toLowerCase() + "@" + getMailDomain();
    }

    private String getContainer() {
        return System.getProperty("container");
    }

    private String getResourceOid() {
        return System.getProperty("resourceOid");
    }

    private ModelPortType modelPort;

    public String getMailDomain() {
        return System.getProperty("mailDomain");
    }

    @BeforeClass
    public void initialize() throws Exception {
        modelPort = createModelPort(new String[0]);
    }

    //    @Test
    //    public void test000() throws InvalidNameException {
    //        System.out.println(distributionGroupOU());
    //        System.exit(0);
    //    }

    @Test
    public void test001GetResource() throws Exception {
        System.out.println("Getting Exchange resource...");
        ResourceType exchangeResource = getResource(getResourceOid());
        if (exchangeResource == null) {
            throw new IllegalStateException("Exchange resource was not found");
        }
        System.out.println("Got it; name = " + getOrig(exchangeResource.getName()));
    }

    // =============== AcceptedDomain ===============

    @Test
    public void test010CreateAcceptedDomain() throws Exception {
        createAndCheckAcceptedDomain("Scientists Domain", "scientists.com", "Authoritative");
    }

    @Test
    public void test012ModifyAcceptedDomain() throws Exception {
        ShadowType domain = getShadowByName(getResourceOid(), OC_ACCEPTED_DOMAIN, "Scientists Domain");
        modifyShadow(domain.getOid(), "attributes/name", ModificationTypeType.REPLACE, "Scientists Domain Updated");
        modifyShadow(domain.getOid(), "attributes/DomainType", ModificationTypeType.REPLACE, "InternalRelay");
        checkAcceptedDomain("Scientists Domain Updated", "scientists.com", "InternalRelay");
    }

    @Test
    public void test015CreateAndDeleteAcceptedDomain() throws Exception {
        String oid = createAndCheckAcceptedDomain("Temporary domain", "temp.com", "Authoritative");
        deleteShadow(getResourceOid(), oid, false);
        acceptedDomains.remove(oid);
        ShadowType tempDomain = getShadowByName(getResourceOid(), OC_ACCEPTED_DOMAIN, "Temporary domain");
        AssertJUnit.assertNull("Temporary domain was not removed", tempDomain);
    }

    private String createAndCheckAcceptedDomain(String name, String domainName, String domainType)
            throws Exception {
        System.out.println("Creating accepted domain " + name);
        String oid = createAcceptedDomain(name, domainName, domainType);
        System.out.println("Done; OID = " + oid);
        acceptedDomains.add(oid);
        return checkAcceptedDomain(name, domainName, domainType).getOid();
    }

    private String createAcceptedDomain(String name, String domainName, String domainType) throws FaultMessage {

        Document doc = ModelClientUtil.getDocumnent();

        ShadowType shadow = new ShadowType();
        shadow.setName(ModelClientUtil.createPolyStringType(name, doc));
        shadow.setResourceRef(createObjectReferenceType(ResourceType.class, getResourceOid()));
        shadow.setObjectClass(OC_ACCEPTED_DOMAIN);
        shadow.setKind(ShadowKindType.GENERIC);
        shadow.setIntent("accepted-domain");

        ShadowAttributesType attributes = new ShadowAttributesType();
        attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_ICFS, "name"), name, doc));
        attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_RI, "DomainName"), domainName, doc));
        attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_RI, "DomainType"), domainType, doc));

        shadow.setAttributes(attributes);

        return createShadow(shadow);
    }

    private ShadowType checkAcceptedDomain(String name, String domainName, String domainType) throws Exception {
        System.out.println("Retrieving AcceptedDomain " + name);
        ShadowType shadowType = getShadowByName(getResourceOid(), OC_ACCEPTED_DOMAIN, name);
        AssertJUnit.assertNotNull("AcceptedDomain " + name + " was not found", shadowType);
        System.out.println("Done; shadow OID = " + shadowType.getOid());
        dumpAttributes(shadowType);
        Map<String, Object> attrs = getAttributesAsMap(shadowType);
        assertAttributeExists(attrs, "uid");
        assertAttributeEquals(attrs, "name", name);
        assertAttributeEquals(attrs, "DomainName", domainName);
        assertAttributeEquals(attrs, "DomainType", domainType);
        return shadowType;
    }

    // =============== GAL ===============

    @Test
    public void test020CreateGlobalAddressList() throws Exception {
        createAndCheckGlobalAddressList("Scientists Global Address List", "Scientist");
    }

    @Test
    public void test022ModifyGlobalAddressList() throws Exception {
        ShadowType gal = getShadowByName(getResourceOid(), OC_GLOBAL_ADDRESS_LIST,
                "Scientists Global Address List");
        modifyShadow(gal.getOid(), "attributes/name", ModificationTypeType.REPLACE,
                "Scientists Global Address List Updated");
        modifyShadow(gal.getOid(), "attributes/RecipientFilter", ModificationTypeType.REPLACE,
                "CustomAttribute1 -eq 'Scientist'");
        checkGlobalAddressList("Scientists Global Address List Updated", "CustomAttribute1 -eq 'Scientist'");
    }

    @Test
    public void test025CreateAndDeleteGlobalAddressList() throws Exception {
        String oid = createAndCheckGlobalAddressList("Temporary GAL", "TEMP");
        deleteShadow(getResourceOid(), oid, false);
        globalAddressLists.remove(oid);
        ShadowType tempGal = getShadowByName(getResourceOid(), OC_GLOBAL_ADDRESS_LIST, "Temporary GAL");
        AssertJUnit.assertNull("Temporary GAL was not removed", tempGal);
    }

    private String createAndCheckGlobalAddressList(String name, String valueToExpect) throws Exception {
        System.out.println("Creating GAL " + name);
        String oid = createGlobalAddressList(name, valueToExpect);
        System.out.println("Done; OID = " + oid);
        globalAddressLists.add(oid);
        return checkGlobalAddressList(name, galFilter(valueToExpect)).getOid();
    }

    private String galFilter(String valueToExpect) {
        return "((Alias -ne $null) -and (CustomAttribute1 -eq '" + valueToExpect + "'))";
    }

    private String createGlobalAddressList(String name, String valueToExpect) throws FaultMessage {

        Document doc = ModelClientUtil.getDocumnent();

        ShadowType shadow = new ShadowType();
        shadow.setName(ModelClientUtil.createPolyStringType(name, doc));
        shadow.setResourceRef(createObjectReferenceType(ResourceType.class, getResourceOid()));
        shadow.setObjectClass(OC_GLOBAL_ADDRESS_LIST);
        shadow.setKind(ShadowKindType.GENERIC);
        shadow.setIntent("global-address-list");

        ShadowAttributesType attributes = new ShadowAttributesType();
        attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_ICFS, "name"), name, doc));
        attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_RI, "RecipientFilter"),
                galFilter(valueToExpect), doc));
        shadow.setAttributes(attributes);

        return createShadow(shadow);
    }

    private ShadowType checkGlobalAddressList(String name, String recipientFilter) throws Exception {
        System.out.println("Retrieving GAL " + name);
        ShadowType shadowType = getShadowByName(getResourceOid(), OC_GLOBAL_ADDRESS_LIST, name);
        AssertJUnit.assertNotNull("GAL " + name + " was not found", shadowType);
        System.out.println("Done; shadow OID = " + shadowType.getOid());
        dumpAttributes(shadowType);
        Map<String, Object> attrs = getAttributesAsMap(shadowType);
        assertAttributeExists(attrs, "uid");
        assertAttributeEquals(attrs, "name", name);
        assertAttributeEquals(attrs, "RecipientFilter", recipientFilter);
        return shadowType;
    }

    // =============== Address Lists ===============

    enum AddressListType {

        USERS("address-list-all-users",
                "((((Alias -ne $null) -and (CustomAttribute1 -eq '$$$'))) -and (ObjectCategory -like 'person'))"), GROUPS(
                        "address-list-all-groups",
                        "((((Alias -ne $null) -and (CustomAttribute1 -eq '$$$'))) -and (ObjectCategory -like 'group'))"), CONTACTS(
                                "address-list-all-contacts",
                                "((((Alias -ne $null) -and (CustomAttribute1 -eq '$$$'))) -and (((ObjectCategory -like 'person') -and (ObjectClass -eq 'contact'))))"), ROOMS(
                                        "address-list-all-rooms",
                                        "((((Alias -ne $null) -and (CustomAttribute1 -eq '$$$'))) -and (((RecipientDisplayType -eq 'ConferenceRoomMailbox') -or (RecipientDisplayType -eq 'SyncedConferenceRoomMailbox'))))");

        private String intent;
        private final String filterTemplate;

        AddressListType(String intent, String filterTemplate) {
            this.intent = intent;
            this.filterTemplate = filterTemplate;
        }

        public String createFilter(String valueToExpect) {
            return filterTemplate.replace("$$$", valueToExpect);
        }

        public String getIntent() {
            return intent;
        }
    }

    @Test
    public void test030CreateAddressLists() throws Exception {
        createAndCheckAddressList("Scientists All Users", "Scientist", AddressListType.USERS);
        createAndCheckAddressList("Scientists All Groups", "Scientist", AddressListType.GROUPS);
        createAndCheckAddressList("Scientists All Contacts", "Scientist", AddressListType.CONTACTS);
        createAndCheckAddressList("Scientists All Rooms", "Scientist", AddressListType.ROOMS);
    }

    @Test
    public void test032ModifyAddressList() throws Exception {
        ShadowType gal = getShadowByName(getResourceOid(), OC_ADDRESS_LIST, "Scientists All Users");
        modifyShadow(gal.getOid(), "attributes/name", ModificationTypeType.REPLACE, "Scientists All Users Updated");
        modifyShadow(gal.getOid(), "attributes/RecipientFilter", ModificationTypeType.REPLACE,
                "CustomAttribute1 -eq 'Scientist'");
        checkAddressList("Scientists All Users Updated", "CustomAttribute1 -eq 'Scientist'");
    }

    @Test
    public void test035CreateAndDeleteAddressList() throws Exception {
        String oid = createAndCheckAddressList("Temporary Address List", "TEMP", AddressListType.USERS);
        deleteShadow(getResourceOid(), oid, false);
        addressLists.remove(oid);
        ShadowType temp = getShadowByName(getResourceOid(), OC_ADDRESS_LIST, "Temporary Address List");
        AssertJUnit.assertNull("Temporary AddressList was not removed", temp);
    }

    private String createAndCheckAddressList(String name, String valueToExpect, AddressListType type)
            throws Exception {
        System.out.println("Creating AddressList " + name);
        String oid = createAddressList(name, valueToExpect, type);
        System.out.println("Done; OID = " + oid);
        addressLists.add(oid);
        return checkAddressList(name, type.createFilter(valueToExpect)).getOid();
    }

    private String createAddressList(String name, String valueToExpect, AddressListType type) throws FaultMessage {

        Document doc = ModelClientUtil.getDocumnent();

        ShadowType shadow = new ShadowType();
        shadow.setName(ModelClientUtil.createPolyStringType(name, doc));
        shadow.setResourceRef(createObjectReferenceType(ResourceType.class, getResourceOid()));
        shadow.setObjectClass(OC_ADDRESS_LIST);
        shadow.setKind(ShadowKindType.GENERIC);
        shadow.setIntent(type.getIntent());

        ShadowAttributesType attributes = new ShadowAttributesType();
        attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_ICFS, "name"), name, doc));
        attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_RI, "RecipientFilter"),
                type.createFilter(valueToExpect), doc));
        shadow.setAttributes(attributes);

        return createShadow(shadow);
    }

    private ShadowType checkAddressList(String name, String recipientFilter) throws Exception {
        System.out.println("Retrieving AddressList " + name);
        ShadowType shadowType = getShadowByName(getResourceOid(), OC_ADDRESS_LIST, name);
        AssertJUnit.assertNotNull("AddressList " + name + " was not found", shadowType);
        System.out.println("Done; shadow OID = " + shadowType.getOid());
        dumpAttributes(shadowType);
        Map<String, Object> attrs = getAttributesAsMap(shadowType);
        assertAttributeExists(attrs, "uid");
        assertAttributeEquals(attrs, "name", name);
        assertAttributeEquals(attrs, "RecipientFilter", recipientFilter);
        return shadowType;
    }

    // =============== OfflineAddressBook ===============

    @Test
    public void test040CreateOfflineAddressBook() throws Exception {
        createAndCheckOfflineAddressBook("Scientists Offline Address Book",
                "Scientists Global Address List Updated", "Scientists");
    }

    @Test
    public void test042ModifyOfflineAddressBook() throws Exception {
        String tempGal = createGlobalAddressList("Scientists Global Address List - Temporary", "TEMP");
        ShadowType shadow = getShadowByName(getResourceOid(), OC_OFFLINE_ADDRESS_BOOK,
                "Scientists Offline Address Book");
        modifyShadow(shadow.getOid(), "attributes/name", ModificationTypeType.REPLACE,
                "Scientists Offline Address Book Updated");
        modifyShadow(shadow.getOid(), "attributes/AddressLists", ModificationTypeType.REPLACE,
                "Scientists Global Address List - Temporary");
        checkOfflineAddressBook("Scientists Offline Address Book Updated",
                "Scientists Global Address List - Temporary");

        modifyShadow(shadow.getOid(), "attributes/AddressLists", ModificationTypeType.REPLACE,
                "Scientists Global Address List Updated");
        checkOfflineAddressBook("Scientists Offline Address Book Updated",
                "Scientists Global Address List Updated");

        deleteShadow(getResourceOid(), tempGal, false);
        globalAddressLists.remove(tempGal);
    }

    @Test
    public void test045CreateAndDeleteOfflineAddressBook() throws Exception {
        String oid = createAndCheckOfflineAddressBook("Temporary OAB", "Scientists Global Address List Updated",
                "Scientists");
        deleteShadow(getResourceOid(), oid, false);
        offlineAddressBooks.remove(oid);
        ShadowType temp = getShadowByName(getResourceOid(), OC_OFFLINE_ADDRESS_BOOK, "Temporary OAB");
        AssertJUnit.assertNull("Temporary OAB was not removed", temp);
    }

    private String createAndCheckOfflineAddressBook(String name, String addressList, String tenantName)
            throws Exception {
        System.out.println("Creating OAB " + name);
        String oid = createOfflineAddressBook(name, addressList, tenantName);
        System.out.println("Done; OID = " + oid);
        offlineAddressBooks.add(oid);
        return checkOfflineAddressBook(name, addressList).getOid();
    }

    private String createOfflineAddressBook(String name, String addressList, String tenantName)
            throws FaultMessage {

        Document doc = ModelClientUtil.getDocumnent();

        ShadowType shadow = new ShadowType();
        shadow.setName(ModelClientUtil.createPolyStringType(name, doc));
        shadow.setResourceRef(createObjectReferenceType(ResourceType.class, getResourceOid()));
        shadow.setObjectClass(OC_OFFLINE_ADDRESS_BOOK);
        shadow.setKind(ShadowKindType.GENERIC);
        shadow.setIntent("offline-address-book");

        ShadowAttributesType attributes = new ShadowAttributesType();
        attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_ICFS, "name"), name, doc));
        attributes.getAny()
                .add(ModelClientUtil.createTextElement(new QName(NS_RI, "AddressLists"), addressList, doc));
        attributes.getAny()
                .add(ModelClientUtil.createTextElement(new QName(NS_RI, "_TenantName"), tenantName, doc));
        shadow.setAttributes(attributes);

        return createShadow(shadow);
    }

    private ShadowType checkOfflineAddressBook(String name, String addressList) throws Exception {
        System.out.println("Retrieving OAB " + name);
        ShadowType shadowType = getShadowByName(getResourceOid(), OC_OFFLINE_ADDRESS_BOOK, name);
        AssertJUnit.assertNotNull("OAB " + name + " was not found", shadowType);
        System.out.println("Done; shadow OID = " + shadowType.getOid());
        dumpAttributes(shadowType);
        Map<String, Object> attrs = getAttributesAsMap(shadowType);
        assertAttributeExists(attrs, "uid");
        assertAttributeEquals(attrs, "name", name);
        assertAttributeEquals(attrs, "AddressLists", "\\" + addressList);
        return shadowType;
    }

    // TODO checking access rights to download OAB

    // =============== AddressBookPolicy ===============

    @Test
    public void test050CreateAddressBookPolicy() throws Exception {
        createAndCheckAddressBookPolicy("Scientists Address Book Policy",
                Arrays.asList("\\Scientists All Users Updated", "\\Scientists All Groups",
                        "\\Scientists All Contacts"),
                "\\Scientists Global Address List Updated", "\\Scientists Offline Address Book Updated",
                "\\Scientists All Rooms");
    }

    @Test
    public void test052ModifyAddressBookPolicy() throws Exception {
        ShadowType shadow = getShadowByName(getResourceOid(), OC_ADDRESS_BOOK_POLICY,
                "Scientists Address Book Policy");
        modifyShadow(shadow.getOid(), "attributes/name", ModificationTypeType.REPLACE,
                "Scientists Address Book Policy Updated");
        // TODO enable this
        //        modifyShadow(shadow.getOid(), "attributes/AddressLists", ModificationTypeType.DELETE, "Scientists All Contacts");
        //        checkAddressBookPolicy("Scientists Address Book Policy Updated",
        //                new HashSet(Arrays.asList("Scientists All Users Updated", "Scientists All Groups")),
        //                "Scientists Global Address List Updated",
        //                "Scientists Offline Address Book Updated",
        //                "Scientists All Rooms");
    }

    @Test
    public void test055CreateAndDeleteAddressBookPolicy() throws Exception {
        String oid = createAndCheckAddressBookPolicy("Temporary Address Book Policy",
                Arrays.asList("\\Scientists All Users Updated", "\\Scientists All Groups",
                        "\\Scientists All Contacts"),
                "\\Scientists Global Address List Updated", "\\Scientists Offline Address Book Updated",
                "\\Scientists All Rooms");

        deleteShadow(getResourceOid(), oid, false);
        addressBookPolicies.remove(oid);
        ShadowType temp = getShadowByName(getResourceOid(), OC_ADDRESS_BOOK_POLICY,
                "Temporary Address Book Policy");
        AssertJUnit.assertNull("Temporary Address Book Policy was not removed", temp);
    }

    private String createAndCheckAddressBookPolicy(String name, Collection<String> addressLists, String gal,
            String oab, String rooms) throws Exception {
        System.out.println("Creating ABP " + name);
        String oid = createAddressBookPolicy(name, addressLists, gal, oab, rooms);
        System.out.println("Done; OID = " + oid);
        addressBookPolicies.add(oid);
        return checkAddressBookPolicy(name, addressLists, gal, oab, rooms).getOid();
    }

    private String createAddressBookPolicy(String name, Collection<String> addressLists, String gal, String oab,
            String rooms) throws FaultMessage {

        Document doc = ModelClientUtil.getDocumnent();

        ShadowType shadow = new ShadowType();
        shadow.setName(ModelClientUtil.createPolyStringType(name, doc));
        shadow.setResourceRef(createObjectReferenceType(ResourceType.class, getResourceOid()));
        shadow.setObjectClass(OC_ADDRESS_BOOK_POLICY);
        shadow.setKind(ShadowKindType.GENERIC);
        shadow.setIntent("address-book-policy");

        ShadowAttributesType attributes = new ShadowAttributesType();
        attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_ICFS, "name"), name, doc));
        for (String addressList : addressLists) {
            attributes.getAny()
                    .add(ModelClientUtil.createTextElement(new QName(NS_RI, "AddressLists"), addressList, doc));
        }
        attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_RI, "GlobalAddressList"), gal, doc));
        attributes.getAny()
                .add(ModelClientUtil.createTextElement(new QName(NS_RI, "OfflineAddressBook"), oab, doc));
        attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_RI, "RoomList"), rooms, doc));
        shadow.setAttributes(attributes);

        return createShadow(shadow);
    }

    private ShadowType checkAddressBookPolicy(String name, Collection<String> addressLists, String gal, String oab,
            String rooms) throws Exception {
        System.out.println("Retrieving ABP " + name);
        ShadowType shadowType = getShadowByName(getResourceOid(), OC_ADDRESS_BOOK_POLICY, name);
        AssertJUnit.assertNotNull("ABP " + name + " was not found", shadowType);
        System.out.println("Done; shadow OID = " + shadowType.getOid());
        dumpAttributes(shadowType);
        Map<String, Object> attrs = getAttributesAsMap(shadowType);
        assertAttributeExists(attrs, "uid");
        assertAttributeEquals(attrs, "name", name);
        assertAttributeEquals(attrs, "AddressLists", new HashSet<>(addressLists));
        assertAttributeEquals(attrs, "GlobalAddressList", gal);
        assertAttributeEquals(attrs, "OfflineAddressBook", oab);
        assertAttributeEquals(attrs, "RoomList", rooms);
        return shadowType;
    }

    // =============== DistributionGroup ===============

    private String distributionGroupName(String name) {
        return "Mail-" + name + "@MailSecurity";
    }

    private String distributionGroupPrimaryAddress(String name) {
        return "Mail-" + name + "@MailSecurity";
    }

    private Collection<String> distributionGroupMembers(String name) {
        return Arrays.asList("Mail-" + name + "@AllUsers");
    }

    private String distributionGroupDisplayName(String name) {
        return "Mail-" + name + "@MailSecurity";
    }

    private String distributionGroupOU() throws InvalidNameException {
        LdapName container = new LdapName(getContainer());
        List<String> ous = new ArrayList<>();
        List<String> dcs = new ArrayList<>();
        String retval = "";
        for (Rdn rdn : container.getRdns()) {
            if (rdn.getType().equalsIgnoreCase("OU")) {
                ous.add(rdn.getValue().toString());
            } else if (rdn.getType().equalsIgnoreCase("DC")) {
                dcs.add(rdn.getValue().toString());
            }
        }
        for (int i = dcs.size() - 1; i >= 0; i--) {
            if (!retval.isEmpty()) {
                retval += ".";
            }
            retval += dcs.get(i);
        }
        for (int i = 0; i < ous.size(); i++) {
            retval += "/" + ous.get(i);
        }
        return retval;
    }

    @Test
    public void test060CreateDistributionGroup() throws Exception {
        String n = "Scientists";
        createAndCheckDistributionGroup(distributionGroupName(n), distributionGroupPrimaryAddress(n),
                distributionGroupMembers(n), distributionGroupOU(), distributionGroupDisplayName(n), "Scientist");
    }

    @Test
    public void test062ModifyDistributionGroup() throws Exception {
        String n = "Scientists";
        String newName = distributionGroupName(n) + "-Updated";
        String newAddress = distributionGroupPrimaryAddress(n) + "-Updated";
        String newDisplayName = distributionGroupDisplayName(n) + " Updated";
        String newCustomAttribute1 = "GreatScientist";
        ShadowType shadow = getShadowByName(getResourceOid(), OC_DISTRIBUTION_GROUP, distributionGroupName(n));
        modifyShadow(shadow.getOid(), "attributes/name", ModificationTypeType.REPLACE, newName);
        modifyShadow(shadow.getOid(), "attributes/PrimarySmtpAddress", ModificationTypeType.REPLACE, newAddress);
        modifyShadow(shadow.getOid(), "attributes/DisplayName", ModificationTypeType.REPLACE, newDisplayName);
        modifyShadow(shadow.getOid(), "attributes/CustomAttribute1", ModificationTypeType.REPLACE,
                newCustomAttribute1);

        checkDistributionGroup(newName, newAddress, distributionGroupMembers(n), distributionGroupOU(),
                newDisplayName, newCustomAttribute1);
    }

    @Test
    public void test065CreateAndDeleteDistributionGroup() throws Exception {
        String n = "TEMP";
        String oid = createAndCheckDistributionGroup(distributionGroupName(n), distributionGroupPrimaryAddress(n),
                distributionGroupMembers("Scientists"), distributionGroupOU(), distributionGroupDisplayName(n),
                "TEMP");

        deleteShadow(getResourceOid(), oid, false);
        distributionGroups.remove(oid);
        ShadowType temp = getShadowByName(getResourceOid(), OC_DISTRIBUTION_GROUP, distributionGroupName(n));
        AssertJUnit.assertNull("Temporary DistributionGroup was not removed", temp);
    }

    private String createAndCheckDistributionGroup(String name, String primaryAddress, Collection<String> members,
            String ou, String displayName, String valueForFilter) throws Exception {
        System.out.println("Creating DG " + name);
        String oid = createDistributionGroup(name, primaryAddress, members, ou, displayName, valueForFilter);
        System.out.println("Done; OID = " + oid);
        distributionGroups.add(oid);
        return checkDistributionGroup(name, primaryAddress, members, ou, displayName, valueForFilter).getOid();
    }

    private String createDistributionGroup(String name, String primaryAddress, Collection<String> members,
            String ou, String displayName, String valueForFilter) throws FaultMessage {

        Document doc = ModelClientUtil.getDocumnent();

        ShadowType shadow = new ShadowType();
        shadow.setName(ModelClientUtil.createPolyStringType(name, doc));
        shadow.setResourceRef(createObjectReferenceType(ResourceType.class, getResourceOid()));
        shadow.setObjectClass(OC_DISTRIBUTION_GROUP);
        shadow.setKind(ShadowKindType.GENERIC);
        shadow.setIntent("distribution-group");

        ShadowAttributesType attributes = new ShadowAttributesType();
        attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_ICFS, "name"), name, doc));
        attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_RI, "Type"), "Security", doc));
        attributes.getAny().add(
                ModelClientUtil.createTextElement(new QName(NS_RI, "PrimarySmtpAddress"), primaryAddress, doc));
        for (String member : members) {
            attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_RI, "Members"), member, doc));
        }
        attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_RI, "OrganizationalUnit"), ou, doc));
        attributes.getAny()
                .add(ModelClientUtil.createTextElement(new QName(NS_RI, "DisplayName"), displayName, doc));
        attributes.getAny().add(
                ModelClientUtil.createTextElement(new QName(NS_RI, "HiddenFromAddressListsEnabled"), "true", doc));
        attributes.getAny()
                .add(ModelClientUtil.createTextElement(new QName(NS_RI, "CustomAttribute1"), valueForFilter, doc));
        attributes.getAny().add(ModelClientUtil
                .createTextElement(new QName(NS_RI, "BypassSecurityGroupManagerCheck"), "true", doc));
        shadow.setAttributes(attributes);

        return createShadow(shadow);
    }

    private ShadowType checkDistributionGroup(String name, String primaryAddress, Collection<String> members,
            String ou, String displayName, String valueForFilter) throws Exception {
        System.out.println("Retrieving DistributionGroup " + name);
        ShadowType shadowType = getShadowByName(getResourceOid(), OC_DISTRIBUTION_GROUP, name);
        AssertJUnit.assertNotNull("DistributionGroup " + name + " was not found", shadowType);
        System.out.println("Done; shadow OID = " + shadowType.getOid());
        dumpAttributes(shadowType);
        Map<String, Object> attrs = getAttributesAsMap(shadowType);
        assertAttributeExists(attrs, "uid");
        assertAttributeEquals(attrs, "name", name);
        assertAttributeEquals(attrs, "RecipientType", "MailUniversalSecurityGroup");
        assertAttributeEquals(attrs, "PrimarySmtpAddress", primaryAddress);
        //Members cannot be retrieved in this way
        //assertAttributeEquals(attrs, "Members", members);
        assertAttributeEquals(attrs, "OrganizationalUnit", ou);
        assertAttributeEquals(attrs, "DisplayName", displayName);
        assertAttributeEquals(attrs, "HiddenFromAddressListsEnabled", "true");
        assertAttributeEquals(attrs, "CustomAttribute1", valueForFilter);
        return shadowType;
    }

    // =============== Users ===============

    @Test
    public void test110CreateNewton() throws Exception {
        System.out.println("Creating account for Newton...");
        newtonOid = createAccount(NEWTON_GIVEN_NAME, NEWTON_SN, "User");
        System.out.println("Done; OID = " + newtonOid);
    }

    @Test
    public void test112GetNewton() throws Exception {
        ShadowType newton = checkAccount(NEWTON_GIVEN_NAME, NEWTON_SN);
        Map<String, Object> attrs = getAttributesAsMap(newton);
        assertAttributeEquals(attrs, "RecipientType", "User");
    }

    @Test
    public void test120CreateLeibniz() throws Exception {
        System.out.println("Creating account for Leibniz...");
        leibnizOid = createAccount(LEIBNIZ_GIVEN_NAME, LEIBNIZ_SN, "UserMailbox");
        System.out.println("Done; OID = " + leibnizOid);
    }

    @Test
    public void test122GetLeibniz() throws Exception {
        String mail = mail(LEIBNIZ_GIVEN_NAME, LEIBNIZ_SN);
        ShadowType leibniz = checkAccount(LEIBNIZ_GIVEN_NAME, LEIBNIZ_SN);
        Map<String, Object> attrs = getAttributesAsMap(leibniz);
        assertAttributeEquals(attrs, "RecipientType", "UserMailbox");
        assertAttributeExists(attrs, "homeMDB");
        assertAttributeEquals(attrs, "PrimarySmtpAddress", mail);
        assertAttributeEquals(attrs, "mail", mail);
        assertAttributeEquals(attrs, "Alias", LEIBNIZ_SN.toLowerCase());
        assertAttributeContains(attrs, "EmailAddresses", "SMTP:" + mail); // FIXME
        assertAttributeEquals(attrs, "EmailAddressPolicyEnabled", "true");
        assertAttributeEquals(attrs, "msExchRecipientDisplayType", "1073741824");
        assertAttributeEquals(attrs, "msExchRecipientTypeDetails", "1");
        assertAttributeEquals(attrs, "displayName", LEIBNIZ_GIVEN_NAME + " " + LEIBNIZ_SN);
    }

    @Test
    public void test130CreatePascal() throws Exception {
        System.out.println("Creating account for Pascal...");
        pascalOid = createAccount(PASCAL_GIVEN_NAME, PASCAL_SN, "UserMailbox");
        System.out.println("Done; OID = " + pascalOid);

        String mail = mail(PASCAL_GIVEN_NAME, PASCAL_SN);
        ShadowType pascal = checkAccount(PASCAL_GIVEN_NAME, PASCAL_SN);
        Map<String, Object> attrs = getAttributesAsMap(pascal);
        assertAttributeEquals(attrs, "RecipientType", "UserMailbox");
        assertAttributeExists(attrs, "homeMDB");
        assertAttributeEquals(attrs, "PrimarySmtpAddress", mail);
        assertAttributeEquals(attrs, "mail", mail);
        assertAttributeEquals(attrs, "Alias", PASCAL_SN.toLowerCase());
        assertAttributeContains(attrs, "EmailAddresses", "SMTP:" + mail); // FIXME
        assertAttributeEquals(attrs, "EmailAddressPolicyEnabled", "true");
        assertAttributeEquals(attrs, "msExchRecipientDisplayType", "1073741824");
        assertAttributeEquals(attrs, "msExchRecipientTypeDetails", "1");
        assertAttributeEquals(attrs, "displayName", PASCAL_GIVEN_NAME + " " + PASCAL_SN);
    }

    @Test
    public void test132AddSecondaryAddressToPascal() throws Exception {
        String mail1 = mail(PASCAL_GIVEN_NAME, PASCAL_SN);
        String mail2 = "pascal@clermont-ferrand.fr";

        System.out.println("Setting new secondary address to Pascal...");
        modifyShadow(pascalOid, "attributes/EmailAddresses", ModificationTypeType.ADD, "smtp:" + mail2);
        System.out.println("Done");

        ShadowType pascal = checkAccount(PASCAL_GIVEN_NAME, PASCAL_SN);
        Map<String, Object> attrs = getAttributesAsMap(pascal);
        assertAttributeEquals(attrs, "PrimarySmtpAddress", mail1);
        assertAttributeEquals(attrs, "mail", mail1);
        assertAttributeEquals(attrs, "Alias", PASCAL_SN.toLowerCase());
        assertAttributeContains(attrs, "EmailAddresses",
                new HashSet<>(Arrays.asList("SMTP:" + mail1, "smtp:" + mail2))); // FIXME
        assertAttributeEquals(attrs, "EmailAddressPolicyEnabled", "true");
        assertAttributeEquals(attrs, "msExchRecipientDisplayType", "1073741824");
        assertAttributeEquals(attrs, "msExchRecipientTypeDetails", "1");
        assertAttributeEquals(attrs, "displayName", PASCAL_GIVEN_NAME + " " + PASCAL_SN);
    }

    @Test
    public void test134SwapAddressesForPascal() throws Exception {
        String mail1 = mail(PASCAL_GIVEN_NAME, PASCAL_SN);
        String mail2 = "pascal@clermont-ferrand.fr";

        System.out.println("Disabling email address policy for Pascal...");
        modifyShadow(pascalOid, "attributes/EmailAddressPolicyEnabled", ModificationTypeType.REPLACE, false);
        System.out.println("Done");
        ShadowType pascal = checkAccount(PASCAL_GIVEN_NAME, PASCAL_SN);
        Map<String, Object> attrs = getAttributesAsMap(pascal);
        assertAttributeEquals(attrs, "EmailAddressPolicyEnabled", "false");

        System.out.println("Setting new email addresses for Pascal...");
        modifyShadow(pascalOid, "attributes/EmailAddresses", ModificationTypeType.REPLACE,
                new HashSet<>(Arrays.asList("smtp:" + mail1, "SMTP:" + mail2)));
        System.out.println("Done");

        pascal = checkAccount(PASCAL_GIVEN_NAME, PASCAL_SN);
        attrs = getAttributesAsMap(pascal);
        assertAttributeEquals(attrs, "PrimarySmtpAddress", mail2);
        assertAttributeEquals(attrs, "mail", mail2);
        assertAttributeEquals(attrs, "Alias", PASCAL_SN.toLowerCase());
        assertAttributeEquals(attrs, "EmailAddresses",
                new HashSet<>(Arrays.asList("SMTP:" + mail2, "smtp:" + mail1)));
        assertAttributeEquals(attrs, "msExchRecipientDisplayType", "1073741824");
        assertAttributeEquals(attrs, "msExchRecipientTypeDetails", "1");
        assertAttributeEquals(attrs, "displayName", PASCAL_GIVEN_NAME + " " + PASCAL_SN);
    }

    // non-existing objects
    @Test
    public void test200FetchingNonexistingPowerShellObject() throws Exception {
        ShadowType domain = getShadowByName(getResourceOid(), OC_ACCEPTED_DOMAIN, "Non-existing domain");
        AssertJUnit.assertNull("Non-existing domain was found somehow", domain);
    }

    @Test
    public void test201FetchingNonexistingAccount() throws Exception {
        ShadowType account = getShadowByName(getResourceOid(), OC_ACCOUNT, "Non-existing user account");
        AssertJUnit.assertNull("Non-existing account was found somehow", account);
    }

    // TODO some tests for modifying non-existing objects

    @Test
    public void test900Cleanup() throws Exception {
        deleteShadow(getResourceOid(), newtonOid, true);
        deleteShadow(getResourceOid(), leibnizOid, true);
        deleteShadow(getResourceOid(), pascalOid, true);
        deleteShadows(getResourceOid(), distributionGroups, true);
        deleteShadows(getResourceOid(), emailAddressPolicies, true);
        deleteShadows(getResourceOid(), addressBookPolicies, true);
        deleteShadows(getResourceOid(), offlineAddressBooks, true);
        deleteShadows(getResourceOid(), addressLists, true);
        deleteShadows(getResourceOid(), globalAddressLists, true);
        deleteShadows(getResourceOid(), acceptedDomains, true);
    }

    private void deleteShadows(String resourceOid, List<String> oids, boolean ignoreFailures) throws FaultMessage {
        for (String oid : oids) {
            deleteShadow(resourceOid, oid, ignoreFailures);
        }
    }

    private void deleteShadow(String resourceOid, String shadowOid, boolean ignoreFailures) throws FaultMessage {
        System.out.println("Deleting shadow " + shadowOid);
        ObjectDeltaType deltaType = new ObjectDeltaType();
        deltaType.setObjectType(ModelClientUtil.getTypeQName(ShadowType.class));
        deltaType.setChangeType(ChangeTypeType.DELETE);
        deltaType.setOid(shadowOid);

        ObjectDeltaListType deltaListType = new ObjectDeltaListType();
        deltaListType.getDelta().add(deltaType);

        ModelExecuteOptionsType executeOptionsType = new ModelExecuteOptionsType();
        if (!ignoreFailures) {
            modelPort.executeChanges(deltaListType, executeOptionsType);
        } else {
            try {
                modelPort.executeChanges(deltaListType, executeOptionsType);
            } catch (Exception e) {
                System.err.println("Cannot remove " + shadowOid + ": " + e.getMessage());
            }
        }
    }

    private void modifyShadow(String oid, String path, ModificationTypeType modType, Object value)
            throws Exception {
        System.out.println("Modifying resource object " + oid + " (" + path + ")");
        ItemDeltaType itemDelta = new ItemDeltaType();
        itemDelta.setModificationType(modType);
        itemDelta.setPath(createNonDefaultItemPathType(path));
        if (!(value instanceof Collection)) {
            itemDelta.getValue().add(value);
        } else {
            itemDelta.getValue().addAll((Collection) value);
        }

        ObjectDeltaType deltaType = new ObjectDeltaType();
        deltaType.setObjectType(ModelClientUtil.getTypeQName(ShadowType.class));
        deltaType.setChangeType(ChangeTypeType.MODIFY);
        deltaType.setOid(oid);
        deltaType.getItemDelta().add(itemDelta);

        ObjectDeltaListType deltaListType = new ObjectDeltaListType();
        deltaListType.getDelta().add(deltaType);
        ObjectDeltaOperationListType odolist = modelPort.executeChanges(deltaListType, null);
        assertExecuteChangesSuccess(odolist);
    }

    private void assertExecuteChangesSuccess(ObjectDeltaOperationListType odolist) {
        for (ObjectDeltaOperationType odo : odolist.getDeltaOperation()) {
            AssertJUnit.assertNotNull("Operation result is null", odo.getExecutionResult());
            if (odo.getExecutionResult().getStatus() != OperationResultStatusType.SUCCESS) {
                System.out.println("!!! Operation result is " + odo.getExecutionResult().getStatus() + ": "
                        + odo.getExecutionResult());
                AssertJUnit.assertEquals("Unexpected operation result status", OperationResultStatusType.SUCCESS,
                        odo.getExecutionResult().getStatus());
            }
        }
    }

    private void assertAttributeExists(Map<String, Object> attrs, String name) {
        AssertJUnit.assertTrue("Attribute " + name + " is missing", attrs.containsKey(name));
    }

    private ShadowType checkAccount(String givenName, String sn) throws Exception {
        String dn = dn(givenName, sn);
        System.out.println("Retrieving " + sn + " account...");
        ShadowType shadowType = getShadowByName(getResourceOid(), OC_ACCOUNT, dn);
        AssertJUnit.assertNotNull(sn + " was not found", shadowType);
        System.out.println("Done; shadow OID = " + shadowType.getOid());
        dumpAttributes(shadowType);
        Map<String, Object> attrs = getAttributesAsMap(shadowType);
        assertAttributeExists(attrs, "uid");
        assertAttributeEquals(attrs, "sAMAccountName", sn.toLowerCase());
        assertAttributeEquals(attrs, "distinguishedName", dn);
        assertAttributeEquals(attrs, "givenName", givenName);
        assertAttributeEquals(attrs, "sn", sn);
        assertAttributeEquals(attrs, "passwordExpired", true);
        assertAttributeEquals(attrs, "PasswordNeverExpires", "false");
        assertAttributeEquals(attrs, "ad_container", getContainer());
        assertAttributeEquals(attrs, "objectClass",
                new HashSet(Arrays.asList("top", "person", "organizationalPerson", "user")));
        return shadowType;
    }

    private void assertAttributeEquals(Map<String, Object> attrs, String name, Object expectedValue) {
        Object realValue = attrs.get(name);
        AssertJUnit.assertEquals("Unexpected value of attribute " + name, expectedValue, realValue);
    }

    private void assertAttributeContains(Map<String, Object> attrs, String name, Object expectedValue) {
        if (expectedValue instanceof Collection) {
            for (Object singleValue : (Collection) expectedValue) {
                assertAttributeContains(attrs, name, singleValue);
            }
        } else {
            Object realValue = attrs.get(name);
            Collection realCollection;
            if (realValue == null) {
                realCollection = new ArrayList();
            } else if (!(realValue instanceof Collection)) {
                realCollection = Arrays.asList(realValue);
            } else {
                realCollection = (Collection) realValue;
            }
            if (!realCollection.contains(expectedValue)) {
                AssertJUnit.assertTrue("Attribute " + name + " was expected to contain " + expectedValue
                        + " but it doesn't: " + realCollection, false);
            }
        }
    }

    private void dumpAttributes(ShadowType shadowType) {
        ShadowAttributesType attributes = shadowType.getAttributes();
        System.out.println("Attributes for " + shadowType.getObjectClass().getLocalPart() + " "
                + getOrig(shadowType.getName()) + " {" + attributes.getAny().size() + " entries):");
        for (Object item : attributes.getAny()) {
            if (item instanceof Element) {
                Element e = (Element) item;
                System.out.println(" - " + e.getLocalName() + ": " + e.getTextContent());
            } else if (item instanceof JAXBElement) {
                JAXBElement je = (JAXBElement) item;
                String typeInfo = je.getValue() instanceof String ? ""
                        : (" (" + je.getValue().getClass().getSimpleName() + ")");
                System.out.println(" - " + je.getName().getLocalPart() + ": " + je.getValue() + typeInfo);
            } else {
                System.out.println(" - " + item);
            }
        }
    }

    private Map<String, Object> getAttributesAsMap(ShadowType shadowType) {
        Map<String, Object> rv = new HashMap<>();

        ShadowAttributesType attributes = shadowType.getAttributes();
        for (Object item : attributes.getAny()) {
            if (item instanceof Element) {
                Element e = (Element) item;
                put(rv, e.getLocalName(), e.getTextContent());
            } else if (item instanceof JAXBElement) {
                JAXBElement je = (JAXBElement) item;
                put(rv, je.getName().getLocalPart(), je.getValue());
            } else {
                // nothing to do here
            }
        }
        return rv;
    }

    private void put(Map<String, Object> map, String name, Object value) {
        Object existing = map.get(name);
        if (existing == null) {
            map.put(name, value);
        } else if (!(existing instanceof Set)) {
            Set set = new HashSet();
            set.add(existing);
            set.add(value);
            map.put(name, set);
        } else {
            ((Set) existing).add(value);
        }
    }

    // TODO move to ModelClientUtil
    private static String getOrig(PolyStringType polyStringType) {
        if (polyStringType == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (Object o : polyStringType.getContent()) {
            if (o instanceof String) {
                sb.append(o);
            } else if (o instanceof Element) {
                Element e = (Element) o;
                if ("orig".equals(e.getLocalName())) {
                    return e.getTextContent();
                }
            } else if (o instanceof JAXBElement) {
                JAXBElement je = (JAXBElement) o;
                if ("orig".equals(je.getName().getLocalPart())) {
                    return (String) je.getValue();
                }
            }
        }
        return sb.toString();
    }

    private static void dump(Collection<? extends ObjectType> objects) {
        System.out.println("Objects returned: " + objects.size());
        for (ObjectType objectType : objects) {
            System.out.println(" - " + getOrig(objectType.getName()) + ": " + objectType);
        }
    }

    private SystemConfigurationType getConfiguration() throws FaultMessage {

        Holder<ObjectType> objectHolder = new Holder<ObjectType>();
        Holder<OperationResultType> resultHolder = new Holder<OperationResultType>();
        SelectorQualifiedGetOptionsType options = new SelectorQualifiedGetOptionsType();

        modelPort.getObject(ModelClientUtil.getTypeQName(SystemConfigurationType.class),
                SystemObjectsType.SYSTEM_CONFIGURATION.value(), options, objectHolder, resultHolder);

        return (SystemConfigurationType) objectHolder.value;
    }

    private Collection<ResourceType> listResources() throws SAXException, IOException, FaultMessage {
        SelectorQualifiedGetOptionsType options = new SelectorQualifiedGetOptionsType();
        Holder<ObjectListType> objectListHolder = new Holder<ObjectListType>();
        Holder<OperationResultType> resultHolder = new Holder<OperationResultType>();

        modelPort.searchObjects(ModelClientUtil.getTypeQName(ResourceType.class), null, options, objectListHolder,
                resultHolder);

        ObjectListType objectList = objectListHolder.value;
        return (Collection) objectList.getObject();
    }

    private ResourceType getResource(String oid) throws SAXException, IOException, FaultMessage {
        Holder<ObjectType> objectHolder = new Holder<>();
        Holder<OperationResultType> resultHolder = new Holder<OperationResultType>();
        SelectorQualifiedGetOptionsType options = new SelectorQualifiedGetOptionsType();

        modelPort.getObject(ModelClientUtil.getTypeQName(ResourceType.class), oid, options, objectHolder,
                resultHolder);

        return (ResourceType) objectHolder.value;
    }

    private Collection<UserType> listUsers() throws SAXException, IOException, FaultMessage {
        SelectorQualifiedGetOptionsType options = new SelectorQualifiedGetOptionsType();
        Holder<ObjectListType> objectListHolder = new Holder<ObjectListType>();
        Holder<OperationResultType> resultHolder = new Holder<OperationResultType>();

        // let's say we want to get first 3 users, sorted alphabetically by user name
        QueryType queryType = new QueryType(); // holds search query + paging options
        PagingType pagingType = new PagingType();
        pagingType.setMaxSize(3);
        pagingType.setOrderBy(ModelClientUtil.createItemPathType("name"));
        pagingType.setOrderDirection(OrderDirectionType.ASCENDING);
        queryType.setPaging(pagingType);

        modelPort.searchObjects(ModelClientUtil.getTypeQName(UserType.class), queryType, options, objectListHolder,
                resultHolder);

        ObjectListType objectList = objectListHolder.value;
        return (Collection) objectList.getObject();
    }

    private Collection<TaskType> listTasks() throws SAXException, IOException, FaultMessage {
        SelectorQualifiedGetOptionsType operationOptions = new SelectorQualifiedGetOptionsType();

        // Let's say we want to retrieve tasks' next scheduled time (because this may be a costly operation if
        // JDBC based quartz scheduler is used, the fetching of this attribute has to be explicitly requested)
        SelectorQualifiedGetOptionType getNextScheduledTimeOption = new SelectorQualifiedGetOptionType();

        // prepare a selector (described by path) + options (saying to retrieve that attribute)
        ObjectSelectorType selector = new ObjectSelectorType();
        selector.setPath(ModelClientUtil.createItemPathType("nextRunStartTimestamp"));
        getNextScheduledTimeOption.setSelector(selector);
        GetOperationOptionsType selectorOptions = new GetOperationOptionsType();
        selectorOptions.setRetrieve(RetrieveOptionType.INCLUDE);
        getNextScheduledTimeOption.setOptions(selectorOptions);

        // add newly created option to the list of operation options
        operationOptions.getOption().add(getNextScheduledTimeOption);

        Holder<ObjectListType> objectListHolder = new Holder<ObjectListType>();
        Holder<OperationResultType> resultHolder = new Holder<OperationResultType>();

        modelPort.searchObjects(ModelClientUtil.getTypeQName(TaskType.class), null, operationOptions,
                objectListHolder, resultHolder);

        ObjectListType objectList = objectListHolder.value;
        return (Collection) objectList.getObject();
    }

    private String createUserGuybrush(RoleType role) throws FaultMessage {
        Document doc = ModelClientUtil.getDocumnent();

        UserType user = new UserType();
        user.setName(ModelClientUtil.createPolyStringType("guybrush", doc));
        user.setFullName(ModelClientUtil.createPolyStringType("Guybrush Threepwood", doc));
        user.setGivenName(ModelClientUtil.createPolyStringType("Guybrush", doc));
        user.setFamilyName(ModelClientUtil.createPolyStringType("Threepwood", doc));
        user.setEmailAddress("guybrush@meleeisland.net");
        user.getOrganization().add(ModelClientUtil.createPolyStringType("Pirate Brethren International", doc));
        user.getOrganizationalUnit().add(ModelClientUtil.createPolyStringType("Pirate Wannabes", doc));
        user.setCredentials(ModelClientUtil.createPasswordCredentials("IwannaBEaPIRATE"));

        if (role != null) {
            // create user with a role assignment
            AssignmentType roleAssignment = createRoleAssignment(role.getOid());
            user.getAssignment().add(roleAssignment);
        }

        return createUser(user);
    }

    private String createAccount(String givenName, String sn, String recipientType) throws FaultMessage {
        String name = dn(givenName, sn);
        String samAccountName = sn.toLowerCase();

        Document doc = ModelClientUtil.getDocumnent();

        ShadowType user = new ShadowType();
        user.setName(ModelClientUtil.createPolyStringType(name, doc));
        user.setResourceRef(createObjectReferenceType(ResourceType.class, getResourceOid()));
        user.setObjectClass(OC_ACCOUNT);
        user.setKind(ShadowKindType.ACCOUNT);

        ShadowAttributesType attributes = new ShadowAttributesType();
        attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_ICFS, "name"), name, doc));
        attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_RI, "givenName"), givenName, doc));
        attributes.getAny().add(ModelClientUtil.createTextElement(new QName(NS_RI, "sn"), sn, doc));
        attributes.getAny()
                .add(ModelClientUtil.createTextElement(new QName(NS_RI, "RecipientType"), recipientType, doc));
        attributes.getAny()
                .add(ModelClientUtil.createTextElement(new QName(NS_RI, "sAMAccountName"), samAccountName, doc));

        user.setAttributes(attributes);

        return createShadow(user);
    }

    private ShadowType getShadowByName(String resourceOid, QName objectClass, String name)
            throws JAXBException, SAXException, IOException, FaultMessage {
        // WARNING: in a real case make sure that the username is properly escaped before putting it in XML
        SearchFilterType filter = ModelClientUtil.parseSearchFilterType(
                "                        <q:and xmlns:q='http://prism.evolveum.com/xml/ns/public/query-3' xmlns:c='http://midpoint.evolveum.com/xml/ns/public/common/common-3'>\n"
                        + "                            <q:ref>\n"
                        + "                                <q:path>resourceRef</q:path>\n"
                        + "                                <q:value>\n"
                        + "                                    <oid>" + resourceOid + "</oid>\n"
                        + "                                    <type>ResourceType</type>\n"
                        + "                                </q:value>\n" + "                            </q:ref>\n"
                        + "                            <q:equal>\n"
                        + "                                <q:path>objectClass</q:path>\n"
                        + "                                <q:value xmlns:a=\"" + objectClass.getNamespaceURI()
                        + "\">a:" + objectClass.getLocalPart() + "</q:value>\n"
                        + "                            </q:equal>\n" + "                            <q:equal>\n"
                        + "                                <q:path>attributes/name</q:path>\n"
                        + "                                <q:value>" + name + "</q:value>\n"
                        + "                            </q:equal>\n" + "                        </q:and>\n");
        QueryType query = new QueryType();
        query.setFilter(filter);
        SelectorQualifiedGetOptionsType options = new SelectorQualifiedGetOptionsType();
        Holder<ObjectListType> objectListHolder = new Holder<>();
        Holder<OperationResultType> resultHolder = new Holder<>();

        modelPort.searchObjects(ModelClientUtil.getTypeQName(ShadowType.class), query, options, objectListHolder,
                resultHolder);

        ObjectListType objectList = objectListHolder.value;
        List<ObjectType> objects = objectList.getObject();
        if (objects.isEmpty()) {
            return null;
        }
        if (objects.size() == 1) {
            return (ShadowType) objects.get(0);
        }
        throw new IllegalStateException("Expected to find a single shadow with name '" + name + "' but found "
                + objects.size() + " ones instead");
    }

    // TODO move to Util
    private static ObjectReferenceType createObjectReferenceType(Class<? extends ObjectType> typeClass,
            String oid) {
        ObjectReferenceType ort = new ObjectReferenceType();
        ort.setOid(oid);
        ort.setType(ModelClientUtil.getTypeQName(typeClass));
        return ort;
    }

    private String createUserFromSystemResource(String resourcePath)
            throws FileNotFoundException, JAXBException, FaultMessage {
        UserType user = unmarshallResource(resourcePath);

        return createUser(user);
    }

    private static <T> T unmarshallFile(File file) throws JAXBException, FileNotFoundException {
        JAXBContext jc = ModelClientUtil.instantiateJaxbContext();
        Unmarshaller unmarshaller = jc.createUnmarshaller();

        InputStream is = null;
        JAXBElement<T> element = null;
        try {
            is = new FileInputStream(file);
            element = (JAXBElement<T>) unmarshaller.unmarshal(is);
        } finally {
            if (is != null) {
                IOUtils.closeQuietly(is);
            }
        }
        if (element == null) {
            return null;
        }
        return element.getValue();
    }

    private static <T> T unmarshallResource(String path) throws JAXBException, FileNotFoundException {
        JAXBContext jc = ModelClientUtil.instantiateJaxbContext();
        Unmarshaller unmarshaller = jc.createUnmarshaller();

        InputStream is = null;
        JAXBElement<T> element = null;
        try {
            is = TestExchangeConnector.class.getClassLoader().getResourceAsStream(path);
            if (is == null) {
                throw new FileNotFoundException("System resource " + path + " was not found");
            }
            element = (JAXBElement<T>) unmarshaller.unmarshal(is);
        } finally {
            if (is != null) {
                IOUtils.closeQuietly(is);
            }
        }
        if (element == null) {
            return null;
        }
        return element.getValue();
    }

    private String createUser(UserType userType) throws FaultMessage {
        ObjectDeltaType deltaType = new ObjectDeltaType();
        deltaType.setObjectType(ModelClientUtil.getTypeQName(UserType.class));
        deltaType.setChangeType(ChangeTypeType.ADD);
        deltaType.setObjectToAdd(userType);

        ObjectDeltaListType deltaListType = new ObjectDeltaListType();
        deltaListType.getDelta().add(deltaType);
        ObjectDeltaOperationListType operationListType = modelPort.executeChanges(deltaListType, null);
        return ModelClientUtil.getOidFromDeltaOperationList(operationListType, deltaType);
    }

    private String createShadow(ShadowType shadowType) throws FaultMessage {
        ObjectDeltaType deltaType = new ObjectDeltaType();
        deltaType.setObjectType(ModelClientUtil.getTypeQName(ShadowType.class));
        deltaType.setChangeType(ChangeTypeType.ADD);
        deltaType.setObjectToAdd(shadowType);

        ObjectDeltaListType deltaListType = new ObjectDeltaListType();
        deltaListType.getDelta().add(deltaType);
        ObjectDeltaOperationListType operationListType = modelPort.executeChanges(deltaListType, null);
        assertExecuteChangesSuccess(operationListType);
        ;
        //        Holder<OperationResultType> holder = new Holder<>();
        //        String oid = getOidFromDeltaOperationList(operationListType, deltaType, holder);
        //        AssertJUnit.assertNotNull("No operation result from create shadow operation", holder.value);
        //        if (holder.value != null && holder.value.getStatus() != OperationResultStatusType.SUCCESS) {
        //            System.out.println("!!! Operation result is " + holder.value.getStatus() + ": " + holder.value);
        //            AssertJUnit.assertEquals("Unexpected operation result status", OperationResultStatusType.SUCCESS, holder.value.getStatus());
        //        }
        return ModelClientUtil.getOidFromDeltaOperationList(operationListType, deltaType);
    }

    /**
     * Retrieves OID and OperationResult created by model Web Service from the returned list of ObjectDeltaOperations.
     *
     * @param operationListType result of the model web service executeChanges call
     * @param originalDelta original request used to find corresponding ObjectDeltaOperationType instance. Must be of ADD type.
     * @param operationResultHolder where the result will be put
     * @return OID if found
     *
     * PRELIMINARY IMPLEMENTATION. Currently the first returned ADD delta with the same object type as original delta is returned.
     *
     * TODO move to ModelClientUtil
     */
    public static String getOidFromDeltaOperationList(ObjectDeltaOperationListType operationListType,
            ObjectDeltaType originalDelta, Holder<OperationResultType> operationResultTypeHolder) {
        Validate.notNull(operationListType);
        Validate.notNull(originalDelta);
        if (originalDelta.getChangeType() != ChangeTypeType.ADD) {
            throw new IllegalArgumentException("Original delta is not of ADD type");
        }
        if (originalDelta.getObjectToAdd() == null) {
            throw new IllegalArgumentException("Original delta contains no object-to-be-added");
        }
        for (ObjectDeltaOperationType operationType : operationListType.getDeltaOperation()) {
            ObjectDeltaType objectDeltaType = operationType.getObjectDelta();
            if (objectDeltaType.getChangeType() == ChangeTypeType.ADD && objectDeltaType.getObjectToAdd() != null) {
                ObjectType objectAdded = (ObjectType) objectDeltaType.getObjectToAdd();
                if (objectAdded.getClass().equals(originalDelta.getObjectToAdd().getClass())) {
                    operationResultTypeHolder.value = operationType.getExecutionResult();
                    return objectAdded.getOid();
                }
            }
        }
        return null;
    }

    private void changeUserPassword(String oid, String newPassword) throws FaultMessage {
        ItemDeltaType passwordDelta = new ItemDeltaType();
        passwordDelta.setModificationType(ModificationTypeType.REPLACE);
        passwordDelta.setPath(ModelClientUtil.createItemPathType("credentials/password/value"));
        passwordDelta.getValue().add(ModelClientUtil.createProtectedString(newPassword));

        ObjectDeltaType deltaType = new ObjectDeltaType();
        deltaType.setObjectType(ModelClientUtil.getTypeQName(UserType.class));
        deltaType.setChangeType(ChangeTypeType.MODIFY);
        deltaType.setOid(oid);
        deltaType.getItemDelta().add(passwordDelta);

        ObjectDeltaListType deltaListType = new ObjectDeltaListType();
        deltaListType.getDelta().add(deltaType);
        modelPort.executeChanges(deltaListType, null);
    }

    private void changeUserGivenName(String oid, String newValue) throws FaultMessage {
        Document doc = ModelClientUtil.getDocumnent();

        ObjectDeltaType userDelta = new ObjectDeltaType();
        userDelta.setOid(oid);
        userDelta.setObjectType(ModelClientUtil.getTypeQName(UserType.class));
        userDelta.setChangeType(ChangeTypeType.MODIFY);

        ItemDeltaType itemDelta = new ItemDeltaType();
        itemDelta.setModificationType(ModificationTypeType.REPLACE);
        itemDelta.setPath(ModelClientUtil.createItemPathType("givenName"));
        itemDelta.getValue().add(ModelClientUtil.createPolyStringType(newValue, doc));
        userDelta.getItemDelta().add(itemDelta);
        ObjectDeltaListType deltaList = new ObjectDeltaListType();
        deltaList.getDelta().add(userDelta);
        modelPort.executeChanges(deltaList, null);
    }

    private void assignRoles(String userOid, String... roleOids) throws FaultMessage {
        modifyRoleAssignment(userOid, true, roleOids);
    }

    private void unAssignRoles(String userOid, String... roleOids) throws FaultMessage {
        modifyRoleAssignment(userOid, false, roleOids);
    }

    private void modifyRoleAssignment(String userOid, boolean isAdd, String... roleOids) throws FaultMessage {
        ItemDeltaType assignmentDelta = new ItemDeltaType();
        if (isAdd) {
            assignmentDelta.setModificationType(ModificationTypeType.ADD);
        } else {
            assignmentDelta.setModificationType(ModificationTypeType.DELETE);
        }
        assignmentDelta.setPath(ModelClientUtil.createItemPathType("assignment"));
        for (String roleOid : roleOids) {
            assignmentDelta.getValue().add(createRoleAssignment(roleOid));
        }

        ObjectDeltaType deltaType = new ObjectDeltaType();
        deltaType.setObjectType(ModelClientUtil.getTypeQName(UserType.class));
        deltaType.setChangeType(ChangeTypeType.MODIFY);
        deltaType.setOid(userOid);
        deltaType.getItemDelta().add(assignmentDelta);

        ObjectDeltaListType deltaListType = new ObjectDeltaListType();
        deltaListType.getDelta().add(deltaType);
        ObjectDeltaOperationListType objectDeltaOperationList = modelPort.executeChanges(deltaListType, null);
        for (ObjectDeltaOperationType objectDeltaOperation : objectDeltaOperationList.getDeltaOperation()) {
            if (!OperationResultStatusType.SUCCESS.equals(objectDeltaOperation.getExecutionResult().getStatus())) {
                System.out.println("*** Operation result = " + objectDeltaOperation.getExecutionResult().getStatus()
                        + ": " + objectDeltaOperation.getExecutionResult().getMessage());
            }
        }
    }

    private AssignmentType createRoleAssignment(String roleOid) {
        AssignmentType roleAssignment = new AssignmentType();
        ObjectReferenceType roleRef = new ObjectReferenceType();
        roleRef.setOid(roleOid);
        roleRef.setType(ModelClientUtil.getTypeQName(RoleType.class));
        roleAssignment.setTargetRef(roleRef);
        return roleAssignment;
    }

    private UserType searchUserByName(String username)
            throws SAXException, IOException, FaultMessage, JAXBException {
        // WARNING: in a real case make sure that the username is properly escaped before putting it in XML
        SearchFilterType filter = ModelClientUtil.parseSearchFilterType(
                "<equal xmlns='http://prism.evolveum.com/xml/ns/public/query-3' xmlns:c='http://midpoint.evolveum.com/xml/ns/public/common/common-3' >"
                        + "<path>c:name</path>" + "<value>" + username + "</value>" + "</equal>");
        QueryType query = new QueryType();
        query.setFilter(filter);
        SelectorQualifiedGetOptionsType options = new SelectorQualifiedGetOptionsType();
        Holder<ObjectListType> objectListHolder = new Holder<ObjectListType>();
        Holder<OperationResultType> resultHolder = new Holder<OperationResultType>();

        modelPort.searchObjects(ModelClientUtil.getTypeQName(UserType.class), query, options, objectListHolder,
                resultHolder);

        ObjectListType objectList = objectListHolder.value;
        List<ObjectType> objects = objectList.getObject();
        if (objects.isEmpty()) {
            return null;
        }
        if (objects.size() == 1) {
            return (UserType) objects.get(0);
        }
        throw new IllegalStateException("Expected to find a single user with username '" + username + "' but found "
                + objects.size() + " users instead");
    }

    private RoleType searchRoleByName(String roleName)
            throws SAXException, IOException, FaultMessage, JAXBException {
        // WARNING: in a real case make sure that the role name is properly escaped before putting it in XML
        SearchFilterType filter = ModelClientUtil.parseSearchFilterType(
                "<equal xmlns='http://prism.evolveum.com/xml/ns/public/query-3' xmlns:c='http://midpoint.evolveum.com/xml/ns/public/common/common-3' >"
                        + "<path>c:name</path>" + "<value>" + roleName + "</value>" + "</equal>");
        QueryType query = new QueryType();
        query.setFilter(filter);
        SelectorQualifiedGetOptionsType options = new SelectorQualifiedGetOptionsType();
        Holder<ObjectListType> objectListHolder = new Holder<ObjectListType>();
        Holder<OperationResultType> resultHolder = new Holder<OperationResultType>();

        modelPort.searchObjects(ModelClientUtil.getTypeQName(RoleType.class), query, options, objectListHolder,
                resultHolder);

        ObjectListType objectList = objectListHolder.value;
        List<ObjectType> objects = objectList.getObject();
        if (objects.isEmpty()) {
            return null;
        }
        if (objects.size() == 1) {
            return (RoleType) objects.get(0);
        }
        throw new IllegalStateException("Expected to find a single role with name '" + roleName + "' but found "
                + objects.size() + " users instead");
    }

    private Collection<RoleType> listRequestableRoles()
            throws SAXException, IOException, FaultMessage, JAXBException {
        SearchFilterType filter = ModelClientUtil.parseSearchFilterType(
                "<equal xmlns='http://prism.evolveum.com/xml/ns/public/query-3' xmlns:c='http://midpoint.evolveum.com/xml/ns/public/common/common-3' >"
                        + "<path>c:requestable</path>" + "<value>true</value>" + "</equal>");
        QueryType query = new QueryType();
        query.setFilter(filter);
        SelectorQualifiedGetOptionsType options = new SelectorQualifiedGetOptionsType();
        Holder<ObjectListType> objectListHolder = new Holder<ObjectListType>();
        Holder<OperationResultType> resultHolder = new Holder<OperationResultType>();

        modelPort.searchObjects(ModelClientUtil.getTypeQName(RoleType.class), query, options, objectListHolder,
                resultHolder);

        ObjectListType objectList = objectListHolder.value;
        return (Collection) objectList.getObject();
    }

    private void deleteUser(String oid) throws FaultMessage {
        ObjectDeltaType deltaType = new ObjectDeltaType();
        deltaType.setObjectType(ModelClientUtil.getTypeQName(UserType.class));
        deltaType.setChangeType(ChangeTypeType.DELETE);
        deltaType.setOid(oid);

        ObjectDeltaListType deltaListType = new ObjectDeltaListType();
        deltaListType.getDelta().add(deltaType);

        ModelExecuteOptionsType executeOptionsType = new ModelExecuteOptionsType();
        executeOptionsType.setRaw(true);
        modelPort.executeChanges(deltaListType, executeOptionsType);
    }

    private void deleteTask(String oid) throws FaultMessage {
        ObjectDeltaType deltaType = new ObjectDeltaType();
        deltaType.setObjectType(ModelClientUtil.getTypeQName(TaskType.class));
        deltaType.setChangeType(ChangeTypeType.DELETE);
        deltaType.setOid(oid);

        ObjectDeltaListType deltaListType = new ObjectDeltaListType();
        deltaListType.getDelta().add(deltaType);

        ModelExecuteOptionsType executeOptionsType = new ModelExecuteOptionsType();
        executeOptionsType.setRaw(true);
        modelPort.executeChanges(deltaListType, executeOptionsType);
    }

    public ModelPortType createModelPort(String[] args) {
        String endpointUrl = DEFAULT_ENDPOINT_URL;

        if (args.length > 0) {
            endpointUrl = args[0];
        }

        System.out.println("Endpoint URL: " + endpointUrl);

        // uncomment this if you want to use Fiddler or any other proxy
        //ProxySelector.setDefault(new MyProxySelector("127.0.0.1", 8888));

        ModelService modelService = new ModelService();
        ModelPortType modelPort = modelService.getModelPort();
        BindingProvider bp = (BindingProvider) modelPort;
        Map<String, Object> requestContext = bp.getRequestContext();
        requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointUrl);

        org.apache.cxf.endpoint.Client client = ClientProxy.getClient(modelPort);
        org.apache.cxf.endpoint.Endpoint cxfEndpoint = client.getEndpoint();

        Map<String, Object> outProps = new HashMap<String, Object>();

        outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
        outProps.put(WSHandlerConstants.USER, ADM_USERNAME);
        outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST);
        outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientPasswordHandler.class.getName());

        WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
        cxfEndpoint.getOutInterceptors().add(wssOut);
        // enable the following to get client-side logging of outgoing requests and incoming responses
        //cxfEndpoint.getOutInterceptors().add(new LoggingOutInterceptor());
        //cxfEndpoint.getInInterceptors().add(new LoggingInInterceptor());

        return modelPort;
    }

    public static ItemPathType createNonDefaultItemPathType(String stringPath) {
        ItemPathType itemPathType = new ItemPathType();
        itemPathType.setValue(stringPath);
        return itemPathType;
    }

}