/*
* Copyright (c) 2010-2017 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.provisioning.ucf.impl.connid;
import static com.evolveum.midpoint.test.IntegrationTestTools.display;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertNotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.test.context.ContextConfiguration;
import org.testng.annotations.Test;
import com.evolveum.icf.dummy.resource.DummyAccount;
import com.evolveum.midpoint.prism.PrismContainerDefinition;
import com.evolveum.midpoint.prism.PrismContainerValue;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.prism.schema.PrismSchema;
import com.evolveum.midpoint.prism.schema.SchemaRegistry;
import com.evolveum.midpoint.prism.util.PrismTestUtil;
import com.evolveum.midpoint.provisioning.ucf.api.GenericFrameworkException;
import com.evolveum.midpoint.provisioning.ucf.api.ResultHandler;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.processor.ObjectClassComplexTypeDefinition;
import com.evolveum.midpoint.schema.processor.ResourceAttribute;
import com.evolveum.midpoint.schema.processor.ResourceAttributeContainer;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.statistics.ConnectorOperationalStatus;
import com.evolveum.midpoint.schema.util.ResourceTypeUtil;
import com.evolveum.midpoint.schema.util.ShadowUtil;
import com.evolveum.midpoint.test.util.TestUtil;
import com.evolveum.midpoint.util.exception.CommunicationException;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SecurityViolationException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorConfigurationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
/**
* UCF test with dummy resource and several connector instances.
*
* @author Radovan Semancik
*/
@ContextConfiguration(locations = { "classpath:ctx-ucf-connid-test.xml" })
public class TestUcfDummyMulti extends AbstractUcfDummyTest {
private static Trace LOGGER = TraceManager.getTrace(TestUcfDummyMulti.class);
@Test
public void test000PrismContextSanity() throws Exception {
final String TEST_NAME = "test000PrismContextSanity";
TestUtil.displayTestTile(TEST_NAME);
SchemaRegistry schemaRegistry = PrismTestUtil.getPrismContext().getSchemaRegistry();
PrismSchema schemaIcfc = schemaRegistry.findSchemaByNamespace(SchemaConstants.NS_ICF_CONFIGURATION);
assertNotNull("ICFC schema not found in the context ("+SchemaConstants.NS_ICF_CONFIGURATION+")", schemaIcfc);
PrismContainerDefinition<ConnectorConfigurationType> configurationPropertiesDef =
schemaIcfc.findContainerDefinitionByElementName(SchemaConstants.CONNECTOR_SCHEMA_CONFIGURATION_PROPERTIES_ELEMENT_QNAME);
assertNotNull("icfc:configurationProperties not found in icfc schema ("+
SchemaConstants.CONNECTOR_SCHEMA_CONFIGURATION_PROPERTIES_ELEMENT_QNAME+")", configurationPropertiesDef);
PrismSchema schemaIcfs = schemaRegistry.findSchemaByNamespace(SchemaConstants.NS_ICF_SCHEMA);
assertNotNull("ICFS schema not found in the context ("+SchemaConstants.NS_ICF_SCHEMA+")", schemaIcfs);
}
@Test
public void test020CreateConfiguredConnector() throws Exception {
final String TEST_NAME = "test020CreateConfiguredConnector";
TestUtil.displayTestTile(TEST_NAME);
cc = connectorFactory.createConnectorInstance(connectorType, ResourceTypeUtil.getResourceNamespace(resourceType),
"test connector");
assertNotNull("Failed to instantiate connector", cc);
OperationResult result = new OperationResult(TestUcfDummyMulti.class.getName() + "." + TEST_NAME);
PrismContainerValue<ConnectorConfigurationType> configContainer = resourceType.getConnectorConfiguration().asPrismContainerValue();
display("Configuration container", configContainer);
// WHEN
cc.configure(configContainer, result);
// THEN
result.computeStatus();
TestUtil.assertSuccess(result);
resourceSchema = cc.fetchResourceSchema(null, result);
assertNotNull("No resource schema", resourceSchema);
}
@Test
public void test100AddAccount() throws Exception {
final String TEST_NAME = "test100AddAccount";
TestUtil.displayTestTile(this, TEST_NAME);
OperationResult result = new OperationResult(this.getClass().getName() + "." + TEST_NAME);
ObjectClassComplexTypeDefinition defaultAccountDefinition = resourceSchema.findDefaultObjectClassDefinition(ShadowKindType.ACCOUNT);
ShadowType shadowType = new ShadowType();
PrismTestUtil.getPrismContext().adopt(shadowType);
shadowType.setName(PrismTestUtil.createPolyStringType(ACCOUNT_JACK_USERNAME));
ObjectReferenceType resourceRef = new ObjectReferenceType();
resourceRef.setOid(resource.getOid());
shadowType.setResourceRef(resourceRef);
shadowType.setObjectClass(defaultAccountDefinition.getTypeName());
PrismObject<ShadowType> shadow = shadowType.asPrismObject();
ResourceAttributeContainer attributesContainer = ShadowUtil.getOrCreateAttributesContainer(shadow, defaultAccountDefinition);
ResourceAttribute<String> icfsNameProp = attributesContainer.findOrCreateAttribute(SchemaConstants.ICFS_NAME);
icfsNameProp.setRealValue(ACCOUNT_JACK_USERNAME);
// WHEN
cc.addObject(shadow, null, null, result);
// THEN
DummyAccount dummyAccount = dummyResource.getAccountByUsername(ACCOUNT_JACK_USERNAME);
assertNotNull("Account "+ACCOUNT_JACK_USERNAME+" was not created", dummyAccount);
assertNotNull("Account "+ACCOUNT_JACK_USERNAME+" has no username", dummyAccount.getName());
}
@Test
public void test110SearchNonBlocking() throws Exception {
final String TEST_NAME = "test100SearchNonBlocking";
TestUtil.displayTestTile(TEST_NAME);
// GIVEN
final ObjectClassComplexTypeDefinition accountDefinition = resourceSchema.findDefaultObjectClassDefinition(ShadowKindType.ACCOUNT);
// Determine object class from the schema
final List<PrismObject<ShadowType>> searchResults = new ArrayList<PrismObject<ShadowType>>();
ResultHandler<ShadowType> handler = new ResultHandler<ShadowType>() {
@Override
public boolean handle(PrismObject<ShadowType> shadow) {
System.out.println("Search: found: " + shadow);
checkUcfShadow(shadow, accountDefinition);
searchResults.add(shadow);
return true;
}
};
OperationResult result = new OperationResult(this.getClass().getName() + "." + TEST_NAME);
// WHEN
cc.search(accountDefinition, new ObjectQuery(), handler, null, null, null, null, result);
// THEN
assertEquals("Unexpected number of search results", 1, searchResults.size());
ConnectorOperationalStatus opStat = cc.getOperationalStatus();
display("stats", opStat);
assertEquals("Wrong pool active", (Integer)0, opStat.getPoolStatusNumActive());
assertEquals("Wrong pool active", (Integer)1, opStat.getPoolStatusNumIdle());
}
@Test
public void test200BlockingSearch() throws Exception {
final String TEST_NAME = "test200BlockingSearch";
TestUtil.displayTestTile(TEST_NAME);
// GIVEN
final OperationResult result = new OperationResult(this.getClass().getName() + "." + TEST_NAME);
final ObjectClassComplexTypeDefinition accountDefinition = resourceSchema.findDefaultObjectClassDefinition(ShadowKindType.ACCOUNT);
// Determine object class from the schema
final List<PrismObject<ShadowType>> searchResults = new ArrayList<PrismObject<ShadowType>>();
final ResultHandler<ShadowType> handler = new ResultHandler<ShadowType>() {
@Override
public boolean handle(PrismObject<ShadowType> shadow) {
checkUcfShadow(shadow, accountDefinition);
searchResults.add(shadow);
return true;
}
};
dummyResource.setBlockOperations(true);
// WHEN
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
cc.search(accountDefinition, new ObjectQuery(), handler, null, null, null, null, result);
} catch (CommunicationException | GenericFrameworkException | SchemaException
| SecurityViolationException | ObjectNotFoundException e) {
LOGGER.error("Error in the search: {}", e.getMessage(), e);
}
}
});
t.setName("search1");
t.start();
// Give the new thread a chance to get blocked
Thread.sleep(500);
ConnectorOperationalStatus opStat = cc.getOperationalStatus();
display("stats (blocked)", opStat);
assertEquals("Wrong pool active", (Integer)1, opStat.getPoolStatusNumActive());
assertEquals("Wrong pool active", (Integer)0, opStat.getPoolStatusNumIdle());
assertEquals("Unexpected number of search results", 0, searchResults.size());
dummyResource.unblock();
t.join();
dummyResource.setBlockOperations(false);
// THEN
assertEquals("Unexpected number of search results", 1, searchResults.size());
opStat = cc.getOperationalStatus();
display("stats (final)", opStat);
assertEquals("Wrong pool active", (Integer)0, opStat.getPoolStatusNumActive());
assertEquals("Wrong pool active", (Integer)1, opStat.getPoolStatusNumIdle());
PrismObject<ShadowType> searchResult = searchResults.get(0);
display("Search result", searchResult);
}
@Test
public void test210TwoBlockingSearches() throws Exception {
final String TEST_NAME = "test210TwoBlockingSearches";
TestUtil.displayTestTile(TEST_NAME);
// GIVEN
final ObjectClassComplexTypeDefinition accountDefinition = resourceSchema.findDefaultObjectClassDefinition(ShadowKindType.ACCOUNT);
// Determine object class from the schema
final OperationResult result1 = new OperationResult(this.getClass().getName() + "." + TEST_NAME);
final List<PrismObject<ShadowType>> searchResults1 = new ArrayList<PrismObject<ShadowType>>();
final ResultHandler<ShadowType> handler1 = new ResultHandler<ShadowType>() {
@Override
public boolean handle(PrismObject<ShadowType> shadow) {
checkUcfShadow(shadow, accountDefinition);
searchResults1.add(shadow);
return true;
}
};
final OperationResult result2 = new OperationResult(this.getClass().getName() + "." + TEST_NAME);
final List<PrismObject<ShadowType>> searchResults2 = new ArrayList<PrismObject<ShadowType>>();
final ResultHandler<ShadowType> handler2 = new ResultHandler<ShadowType>() {
@Override
public boolean handle(PrismObject<ShadowType> shadow) {
checkUcfShadow(shadow, accountDefinition);
searchResults2.add(shadow);
return true;
}
};
dummyResource.setBlockOperations(true);
// WHEN
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
cc.search(accountDefinition, new ObjectQuery(), handler1, null, null, null, null, result1);
} catch (CommunicationException | GenericFrameworkException | SchemaException
| SecurityViolationException | ObjectNotFoundException e) {
LOGGER.error("Error in the search: {}", e.getMessage(), e);
}
}
});
t1.setName("search1");
t1.start();
// Give the new thread a chance to get blocked
Thread.sleep(500);
ConnectorOperationalStatus opStat = cc.getOperationalStatus();
display("stats (blocked 1)", opStat);
assertEquals("Wrong pool active", (Integer)1, opStat.getPoolStatusNumActive());
assertEquals("Wrong pool active", (Integer)0, opStat.getPoolStatusNumIdle());
assertEquals("Unexpected number of search results", 0, searchResults1.size());
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
cc.search(accountDefinition, new ObjectQuery(), handler2, null, null, null, null, result2);
} catch (CommunicationException | GenericFrameworkException | SchemaException
| SecurityViolationException | ObjectNotFoundException e) {
LOGGER.error("Error in the search: {}", e.getMessage(), e);
}
}
});
t2.setName("search2");
t2.start();
// Give the new thread a chance to get blocked
Thread.sleep(500);
opStat = cc.getOperationalStatus();
display("stats (blocked 2)", opStat);
assertEquals("Wrong pool active", (Integer)2, opStat.getPoolStatusNumActive());
assertEquals("Wrong pool active", (Integer)0, opStat.getPoolStatusNumIdle());
assertEquals("Unexpected number of search results", 0, searchResults1.size());
dummyResource.unblockAll();
t1.join();
t2.join();
dummyResource.setBlockOperations(false);
// THEN
assertEquals("Unexpected number of search results 1", 1, searchResults1.size());
assertEquals("Unexpected number of search results 2", 1, searchResults2.size());
opStat = cc.getOperationalStatus();
display("stats (final)", opStat);
assertEquals("Wrong pool active", (Integer)0, opStat.getPoolStatusNumActive());
assertEquals("Wrong pool active", (Integer)2, opStat.getPoolStatusNumIdle());
PrismObject<ShadowType> searchResult1 = searchResults1.get(0);
display("Search result 1", searchResult1);
PrismObject<ShadowType> searchResult2 = searchResults2.get(0);
display("Search result 2", searchResult2);
}
private void checkUcfShadow(PrismObject<ShadowType> shadow, ObjectClassComplexTypeDefinition objectClassDefinition) {
assertNotNull("No objectClass in shadow "+shadow, shadow.asObjectable().getObjectClass());
assertEquals("Wrong objectClass in shadow "+shadow, objectClassDefinition.getTypeName(), shadow.asObjectable().getObjectClass());
Collection<ResourceAttribute<?>> attributes = ShadowUtil.getAttributes(shadow);
assertNotNull("No attributes in shadow "+shadow, attributes);
assertFalse("Empty attributes in shadow "+shadow, attributes.isEmpty());
}
}