Skip to content

Commit

Permalink
Clening up UCF, skeleton of manual connector
Browse files Browse the repository at this point in the history
  • Loading branch information
semancik committed Apr 3, 2017
1 parent 8da59c3 commit 4fcf185
Show file tree
Hide file tree
Showing 19 changed files with 680 additions and 276 deletions.
Expand Up @@ -357,6 +357,8 @@ public abstract class SchemaConstants {

public static final String INTENT_DEFAULT = "default";

public static final String CONNECTOR_SCHEMA_CONFIGURATION_TYPE_LOCAL_NAME = "ConfigurationType";

// This constant should not be here. It is used by schema processor to
// supply correct import. But the dependency should
// be inverted, eventually (MID-356)
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2016 Evolveum
* 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.
Expand Down Expand Up @@ -32,15 +32,18 @@
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;

import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;

/**
* @author Peter Prochazka
* @author Radovan Semancik
*
*/
public class ClassPathUtil {

Expand All @@ -51,13 +54,23 @@ public static Set<Class> listClasses(Package pkg) {
}

public static Set<Class> listClasses(String packageName) {

Set<Class> classes = new HashSet<Class>();

searchClasses(packageName, c -> classes.add(c));
return classes;
}

/**
* This is not entirely reliable method.
* Maybe it would be better to rely on Spring ClassPathScanningCandidateComponentProvider
*/
public static void searchClasses(String packageName, Consumer<Class> consumer) {
String path = packageName.replace('.', '/');
Enumeration<URL> resources = null;
try {// HACK this is not available use LOGGER
resources = LOGGER.getClass().getClassLoader().getResources(path);
// HACK this is not available use LOGGER
ClassLoader classLoader = LOGGER.getClass().getClassLoader();
LOGGER.trace("Classloader: {} : {}", classLoader, classLoader.getClass());
try {
resources = classLoader.getResources(path);
} catch (IOException e) {
LOGGER.error("Classloader scaning error for " + path, e);
}
Expand All @@ -69,15 +82,14 @@ public static Set<Class> listClasses(String packageName) {
// test if it is a directory or JAR
String protocol = candidateUrl.getProtocol();
if ("file".contentEquals(protocol)) {
classes.addAll(getFromDirectory(candidateUrl, packageName));
getFromDirectory(candidateUrl, packageName, consumer);
} else if ("jar".contentEquals(protocol) || "zip".contentEquals(protocol)) {
classes.addAll(getFromJar(candidateUrl, packageName));
getFromJar(candidateUrl, packageName, consumer);
} else {
LOGGER.warn("Unsupported protocol for candidate URL {}", candidateUrl);
}
}

return classes;
}

/**
Expand Down Expand Up @@ -229,9 +241,7 @@ public static boolean extractFilesFromClassPath(String srcPath, String dstPath,
* @return
*/
@SuppressWarnings("rawtypes")
private static Collection<? extends Class> getFromJar(URL srcUrl, String packageName) {
@SuppressWarnings("rawtypes")
Set<Class> classes = new HashSet<Class>();
private static void getFromJar(URL srcUrl, String packageName, Consumer<Class> consumer) {
// sample:
// file:/C:/.m2/repository/test-util/1.9-SNAPSHOT/test-util-1.9-SNAPSHOT.jar!/test-data/opendj.template
// output:
Expand All @@ -254,7 +264,7 @@ private static Collection<? extends Class> getFromJar(URL srcUrl, String package
jarTmp = new File(new URI(srcName));
} catch (URISyntaxException ex) {
LOGGER.error("Error converting jar " + srcName + " name to URI:", ex);
return classes;
return;
}

if (!jarTmp.isFile()) {
Expand All @@ -266,7 +276,7 @@ private static Collection<? extends Class> getFromJar(URL srcUrl, String package
jar = new JarFile(jarTmp);
} catch (IOException ex) {
LOGGER.error("Error during open JAR " + srcName, ex);
return classes;
return;
}
String path = packageName.replace('.', '/');
Enumeration<JarEntry> entries = jar.entries();
Expand All @@ -293,7 +303,8 @@ private static Collection<? extends Class> getFromJar(URL srcUrl, String package
try {// to create class

// Convert name back to package
classes.add(Class.forName(name.replace('/', '.').replace(".class", "")));
Class clazz = Class.forName(name.replace('/', '.').replace(".class", ""));
consumer.accept(clazz);
} catch (ClassNotFoundException ex) {
LOGGER.error("Error during loading class {} from {}. ", name, jar.getName());
}
Expand All @@ -303,10 +314,9 @@ private static Collection<? extends Class> getFromJar(URL srcUrl, String package
jar.close();
} catch (IOException ex) {
LOGGER.error("Error during close JAR {} " + srcName, ex);
return classes;
return;
}

return classes;
}

/**
Expand All @@ -317,24 +327,21 @@ private static Collection<? extends Class> getFromJar(URL srcUrl, String package
* @return
*/
@SuppressWarnings("rawtypes")
private static Collection<Class> getFromDirectory(URL candidateUrl, String packageName) {

@SuppressWarnings("rawtypes")
Set<Class> classes = new HashSet<Class>();
private static void getFromDirectory(URL candidateUrl, String packageName, Consumer<Class> consumer) {

// Directory preparation
File dir = null;
try {
dir = new File(candidateUrl.toURI());
} catch (URISyntaxException e) {
LOGGER.error("NEVER HAPPEND -- Wrong URI: " + candidateUrl.getPath(), e);
return classes;
return;
}

// Skip if it is directory
if (!dir.isDirectory()) {
LOGGER.warn(" Skip: {} is not a directory", candidateUrl.getPath());
return classes;
return;
}

// List directory
Expand All @@ -346,11 +353,11 @@ private static Collection<Class> getFromDirectory(URL candidateUrl, String packa
}
try {// to create class
LOGGER.trace("DIR Candidate: {}", dirList[i]);
classes.add(Class.forName(packageName + "." + dirList[i].replace(".class", "")));
Class<?> clazz = Class.forName(packageName + "." + dirList[i].replace(".class", ""));
consumer.accept(clazz);
} catch (ClassNotFoundException e) {
LOGGER.error("Error during loading class {} from {}. ", dirList[i], dir.getAbsolutePath());
}
}
return classes;
}
}
Expand Up @@ -71,6 +71,7 @@
import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorHostType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.XmlSchemaType;

/**
* Class that manages the ConnectorType objects in repository.
Expand Down Expand Up @@ -407,42 +408,10 @@ public Set<ConnectorType> discoverConnectors(ConnectorHostType hostType, Operati
foundConnector.setConnectorHost(hostType);
}

// Connector schema is normally not generated.
// Let's instantiate the connector and generate the schema
ConnectorInstance connectorInstance = null;
try {
connectorInstance = connectorFactory.createConnectorInstance(foundConnector, null, "discovered connector");
PrismSchema connectorSchema = connectorInstance.generateConnectorSchema();
if (connectorSchema == null) {
LOGGER.warn("Connector {} haven't provided configuration schema", foundConnector);
} else {
LOGGER.trace("Generated connector schema for {}: {} definitions",
foundConnector, connectorSchema.getDefinitions().size());
Document xsdDoc = null;
// Convert to XSD
xsdDoc = connectorSchema.serializeToXsd();
Element xsdElement = DOMUtil.getFirstChildElement(xsdDoc);
LOGGER.trace("Generated XSD connector schema: {}", DOMUtil.serializeDOMToString(xsdElement));
ConnectorTypeUtil.setConnectorXsdSchema(foundConnector, xsdElement);
}
} catch (ObjectNotFoundException ex) {
LOGGER.error(
"Cannot instantiate discovered connector " + ObjectTypeUtil.toShortString(foundConnector),
ex);
result.recordPartialError(
"Cannot instantiate discovered connector " + ObjectTypeUtil.toShortString(foundConnector),
ex);
// Skipping schema generation, but otherwise going on
} catch (SchemaException e) {
LOGGER.error(
"Error processing connector schema for " + ObjectTypeUtil.toShortString(foundConnector)
+ ": " + e.getMessage(), e);
result.recordPartialError(
"Error processing connector schema for " + ObjectTypeUtil.toShortString(foundConnector)
+ ": " + e.getMessage(), e);
// Skipping schema generation, but otherwise going on
if (foundConnector.getSchema() == null) {
LOGGER.warn("Connector {} haven't provided configuration schema", foundConnector);
}

// Sanitize framework-supplied OID
if (StringUtils.isNotEmpty(foundConnector.getOid())) {
LOGGER.warn("Provisioning framework " + foundConnector.getFramework()
Expand Down
Expand Up @@ -91,7 +91,7 @@ public void test001Connectors() throws Exception {
IntegrationTestTools.assertConnectorSchemaSanity(conn, prismContext);
}

assertEquals("Unexpected number of connectors found", 6, connectors.size());
assertEquals("Unexpected number of connectors found", 7, connectors.size());
}

@Test
Expand All @@ -111,7 +111,7 @@ public void testListConnectors() throws Exception{
System.out.println("-----\n");
}

assertEquals("Unexpected number of connectors found", 6, connectors.size());
assertEquals("Unexpected number of connectors found", 7, connectors.size());
}

@Test
Expand Down
Expand Up @@ -71,17 +71,12 @@ public class TestSynchronization extends AbstractIntegrationTest {

private ResourceType resourceType;

@Autowired
private ConnectorFactory manager;

@Autowired
@Autowired(required=true)
private ProvisioningService provisioningService;

@Autowired
@Autowired(required=true)
private ResourceObjectChangeListener syncServiceMock;

private Task syncTask = null;

@BeforeClass
public static void startLdap() throws Exception {
openDJController.startCleanServer();
Expand All @@ -103,7 +98,6 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti
// We need to switch off the encryption checks. Some values cannot be encrypted as we do
// not have a definition here
InternalsConfig.encryptionChecks = false;
assertNotNull(manager);
// let provisioning discover the connectors
provisioningService.postInit(initResult);

Expand Down
4 changes: 4 additions & 0 deletions provisioning/ucf-api/pom.xml
Expand Up @@ -53,6 +53,10 @@
<artifactId>task-api</artifactId>
<version>3.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</dependency>
<dependency>
<groupId>com.evolveum.midpoint.tools</groupId>
<artifactId>test-ng</artifactId>
Expand Down
Expand Up @@ -15,6 +15,7 @@
*/
package com.evolveum.midpoint.provisioning.ucf.api;

import com.evolveum.midpoint.prism.schema.PrismSchema;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.util.exception.CommunicationException;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
Expand Down Expand Up @@ -49,6 +50,8 @@
public interface ConnectorFactory {

String OPERATION_LIST_CONNECTOR = ConnectorFactory.class+".listConnectors";

PrismSchema generateConnectorConfigurationSchema(ConnectorType connectorType) throws ObjectNotFoundException;

/**
* Creates new unconfigured instance of the connector.
Expand Down Expand Up @@ -100,4 +103,5 @@ public interface ConnectorFactory {
String getFrameworkVersion();

void shutdown();

}
Expand Up @@ -77,8 +77,6 @@ public interface ConnectorInstance {

ConnectorOperationalStatus getOperationalStatus() throws ObjectNotFoundException;

PrismSchema generateConnectorSchema();

/**
* Get necessary information from the remote system.
*
Expand Down
@@ -0,0 +1,35 @@
/**
* Copyright (c) 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.api;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(TYPE)
/**
* @author Radovan Semancik
*
*/
public @interface ManagedConnector {

String type();
String version() default "1.0.0";

}

0 comments on commit 4fcf185

Please sign in to comment.