Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: addition of datasource plugin handler #143

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,9 @@ A resource plugin is a generic concept that can be implemented by each product.
The Gravitee Plugin Service Discovery module allows for loading service discovery plugin by providing a specific plugin handler.

Service discovery plugins are useful to dynamically register or deregister endpoints. There is currently one implementation supporting Consul.

=== Datasources

The Gravitee Plugin Datasource module provides a datasource plugin handler in charge of detecting and loading all the datasource plugins.

Datasource plugins are useful to provide a common connection pool or client to a data storage. There is currently one implementation supporting Redis.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@
import io.gravitee.plugin.core.api.Plugin;
import io.gravitee.plugin.core.api.PluginEvent;
import io.gravitee.plugin.core.api.PluginHandler;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import lombok.AccessLevel;
import lombok.Getter;
Expand All @@ -40,10 +47,24 @@ public abstract class AbstractPluginEventListener
implements EventListener<PluginEvent, Plugin> {

public static final String SECRET_PROVIDER = "secret-provider";
public static final String DATASOURCE = "datasource";
public static final String CLUSTER = "cluster";
public static final String CACHE = "cache";
public static final String REPOSITORY = "repository";
public static final String ALERT = "alert";
public static final String COCKPIT = "cockpit";
/**
* Allows to define priority between the different plugin types.
*/
private static final List<String> pluginPriority = Arrays.asList(SECRET_PROVIDER, "cluster", "cache", "repository", "alert", "cockpit");
private static final List<String> pluginPriority = Arrays.asList(
SECRET_PROVIDER,
CLUSTER,
DATASOURCE,
CACHE,
REPOSITORY,
ALERT,
COCKPIT
);

private final Collection<PluginHandler> pluginHandlers;

Expand Down
49 changes: 49 additions & 0 deletions gravitee-plugin-datasource/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!--

Copyright © 2015 The Gravitee team (http://gravitee.io)

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.

-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.gravitee.plugin</groupId>
<artifactId>gravitee-plugin</artifactId>
<version>4.1.0</version>
</parent>

<artifactId>gravitee-plugin-datasource</artifactId>
<packaging>jar</packaging>

<name>Gravitee.io - Plugin - Datasource</name>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>io.gravitee.datasource</groupId>
<artifactId>gravitee-datasource-api</artifactId>
<version>${gravitee-datasource-api.version}</version>
</dependency>

<dependency>
<groupId>io.gravitee.plugin</groupId>
<artifactId>gravitee-plugin-core</artifactId>
</dependency>

</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright © 2015 The Gravitee team (http://gravitee.io)
*
* 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 io.gravitee.plugin.datasource;

import io.gravitee.datasource.api.Datasource;
import io.gravitee.datasource.api.DatasourceConfiguration;
import io.gravitee.datasource.api.DatasourceConfigurationMapper;
import io.gravitee.plugin.core.api.ConfigurablePlugin;
import io.gravitee.plugin.core.api.Plugin;

public interface DatasourcePlugin<C extends DatasourceConfiguration, CM extends DatasourceConfigurationMapper>
extends ConfigurablePlugin<C> {
String PLUGIN_TYPE = "datasource";

Plugin pluginDefinition();

Class<? extends Datasource> datasource();

Class<CM> configurationMapper();

@Override
default String type() {
return PLUGIN_TYPE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* Copyright © 2015 The Gravitee team (http://gravitee.io)
*
* 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 io.gravitee.plugin.datasource;

import static org.springframework.util.StringUtils.hasText;

import io.gravitee.datasource.api.Datasource;
import io.gravitee.datasource.api.DatasourceConfiguration;
import io.gravitee.plugin.core.api.AbstractPluginHandler;
import io.gravitee.plugin.core.api.Plugin;
import io.gravitee.plugin.core.api.PluginContextFactory;
import io.gravitee.plugin.datasource.internal.DatasourceConfigurationClassFinder;
import io.gravitee.plugin.datasource.internal.DatasourceConfigurationFactory;
import io.gravitee.plugin.datasource.internal.DatasourceConfigurationMapperClassFinder;
import io.gravitee.plugin.datasource.spring.DatasourcePluginConfiguration;
import java.net.URLClassLoader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;

/**
* @author Eric LELEU (eric.leleu at graviteesource.com)
* @author GraviteeSource Team
*/
@Import(DatasourcePluginConfiguration.class)
public class DatasourcePluginHandler extends AbstractPluginHandler {

private static final String CONF_DATASOURCES = "datasources";
public static final String DATASOURCE_ID = "id";
public static final String DATASOURCE_ENABLED = "enabled";
public static final String DATASOURCE_SETTINGS = "settings";

@Autowired
private DatasourcePluginManager datasourcePluginManager;

@Autowired
private Environment environment;

@Autowired
private PluginContextFactory pluginContextFactory;

@Autowired
private DatasourceConfigurationFactory configurationFactory;

@Override
protected String type() {
return DatasourcePlugin.PLUGIN_TYPE;
}

@Override
protected ClassLoader getClassLoader(Plugin plugin) throws Exception {
return new URLClassLoader(plugin.dependencies(), this.getClass().getClassLoader());
}

@Override
protected void handle(Plugin plugin, Class<?> pluginClass) {
// register plugin definition to make DS available
// through UI not only through gravitee.yaml
DatasourcePlugin datasourcePlugin = create(plugin, pluginClass);
register(datasourcePlugin);

// instantiate plugin form gravitee.yaml
if (plugin.deployed()) {
var pluginIndex = 0;
while (environment.containsProperty(datasourceConfigEntries(plugin, pluginIndex, DATASOURCE_ID))) {
instantiate(datasourcePlugin, pluginIndex++);
}
}
}

private void instantiate(DatasourcePlugin datasourcePlugin, int pluginIndex) {
final var plugin = datasourcePlugin.pluginDefinition();
if (environment.getProperty(datasourceConfigEntries(plugin, pluginIndex, DATASOURCE_ENABLED), boolean.class, true)) {
final var instanceName = environment.getProperty(datasourceConfigEntries(plugin, pluginIndex, DATASOURCE_ID), String.class);
if (!hasText(instanceName)) {
logger.warn("name is missing for datasource plugin {}, skip the instance creation", plugin.id());
return;
}

logger.info("Loading datasource plugin {} with name {}", plugin.id(), instanceName);
try {
DatasourceConfiguration configuration = configurationFactory.build(
datasourcePlugin.configurationMapper(),
datasourceConfigEntries(plugin, pluginIndex, DATASOURCE_SETTINGS)
);
final var ds = createInstance(datasourcePlugin, configuration);
ds.start();

this.datasourcePluginManager.addDatasource(plugin, instanceName, ds);
} catch (Exception e) {
logger.error("Unable to instantiate datasource plugin of type {}", plugin.type(), e);
}
}
}

private static String datasourceConfigEntries(Plugin plugin, int pluginIndex, String key) {
return CONF_DATASOURCES + "." + plugin.id() + "[" + pluginIndex + "]." + key;
}

public Datasource createInstance(DatasourcePlugin datasourcePlugin, DatasourceConfiguration config) {
var pluginContext = pluginContextFactory.create(datasourcePlugin);
var ds = (Datasource) pluginContext.getAutowireCapableBeanFactory().createBean(datasourcePlugin.datasource());
ds.setConfiguration(config);
return ds;
}

protected DatasourcePlugin create(Plugin plugin, Class<?> pluginClass) {
DatasourcePluginImpl resourcePlugin = new DatasourcePluginImpl(plugin, pluginClass);
resourcePlugin.setConfiguration(new DatasourceConfigurationClassFinder().lookupFirst(pluginClass));
resourcePlugin.setConfigurationMapper(new DatasourceConfigurationMapperClassFinder().lookupFirst(pluginClass));
return resourcePlugin;
}

protected void register(DatasourcePlugin plugin) {
datasourcePluginManager.register(plugin);
}

@Override
public boolean canHandle(Plugin plugin) {
return type().equalsIgnoreCase(plugin.type());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright © 2015 The Gravitee team (http://gravitee.io)
*
* 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 io.gravitee.plugin.datasource;

import io.gravitee.datasource.api.DatasourceConfiguration;
import io.gravitee.datasource.api.DatasourceConfigurationMapper;
import io.gravitee.plugin.core.api.Plugin;
import io.gravitee.plugin.core.api.PluginManifest;
import java.net.URL;
import java.nio.file.Path;

/**
* @author Eric LELEU (eric.leleu at graviteesource.com)
* @author GraviteeSource Team
*/
public class DatasourcePluginImpl implements DatasourcePlugin {

private final Plugin plugin;
private final Class<?> datasourceClass;
private Class<? extends DatasourceConfiguration> configuration;
private Class<? extends DatasourceConfigurationMapper> configurationMapper;

public DatasourcePluginImpl(final Plugin plugin, final Class<?> datasourceClass) {
this.plugin = plugin;
this.datasourceClass = datasourceClass;
}

@Override
public Class<?> datasource() {
return datasourceClass;
}

@Override
public Plugin pluginDefinition() {
return plugin;
}

@Override
public String clazz() {
return plugin.clazz();
}

@Override
public URL[] dependencies() {
return plugin.dependencies();
}

@Override
public String id() {
return plugin.id();
}

@Override
public PluginManifest manifest() {
return plugin.manifest();
}

@Override
public Path path() {
return plugin.path();
}

@Override
public boolean deployed() {
return plugin.deployed();
}

@Override
public Class configuration() {
return configuration;
}

public void setConfiguration(Class<? extends DatasourceConfiguration> datasourceConfigurationClass) {
this.configuration = datasourceConfigurationClass;
}

@Override
public Class configurationMapper() {
return configurationMapper;
}

public void setConfigurationMapper(Class<? extends DatasourceConfigurationMapper> datasourceConfigurationMapperClass) {
this.configurationMapper = datasourceConfigurationMapperClass;
}
}
Loading