Skip to content

Commit

Permalink
Removed OSGi (#539)
Browse files Browse the repository at this point in the history
OSGi was fully removed. As a consequence, lots of files were modified. The main areas impacted are:

- the packaging & bootstraping projects were simplified.
- the integration tests base class was simplified
- the web server is now directly Jetty again (OSGi was using a whiteboard service itself relying on Jetty)
- the client API is now clearer with specific DB and WS helpers
- all plugin searches now rely on Java-standard ServiceLoader
  • Loading branch information
marcanpilami authored Jul 29, 2024
1 parent 7a6ad0f commit a78ad41
Show file tree
Hide file tree
Showing 203 changed files with 2,256 additions and 5,147 deletions.
12 changes: 5 additions & 7 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"LineCount.includes": [
"**/*.java",
"**/*.js",
"**/*.ts",
"**/*.html",
],
"LineCount.excludes": [
Expand All @@ -41,7 +42,8 @@
"**/archetype-resources/**",
"**/META-INF/maven/**",
"**/target/",
"**/webapp/dist/"
"**/webapp/dist/",
"**/react/",
],
"java.autobuild.enabled": true,
"java.test.defaultConfig": "it",
Expand All @@ -54,14 +56,11 @@
"java.naming/javax.naming.spi=ALL-UNNAMED"
],
},
{
"name": "web",
"workingDirectory": "${workspaceFolder}/jqm-all/jqm-integration-tests-ws"
}
],
"java.maven.downloadSources": true,
"java.import.gradle.enabled": false,
"java.import.maven.enabled": true,
"java.compile.nullAnalysis.mode": "automatic",
"java.codeGeneration.useBlocks": true,
"maven.excludedFolders": [
"**/.*", // exclude hidden folders
Expand All @@ -78,5 +77,4 @@
"restructuredtext.confPath": "${workspaceFolder}\\jqm-all\\jqm-doc\\src\\site\\sphinx",
"esbonio.sphinx.confDir": "${workspaceFolder}\\jqm-all\\jqm-doc\\src\\site\\sphinx",
"restructuredtext.builtDocumentationPath": "${workspaceFolder}/jqm-all/jqm-doc/src/site/sphinx/_build/html",
"restructuredtext.languageServer.disabled": true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,4 @@
*
*
*/
@org.osgi.annotation.bundle.Export
@org.osgi.annotation.versioning.Version("3.0.0")
package com.enioka.admin;
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,4 @@
* The classes are XML annotated, so their XML and JSON representation are part of the API.
*
*/
@org.osgi.annotation.bundle.Export
@org.osgi.annotation.versioning.Version("3.0.0")
package com.enioka.api.admin;
9 changes: 1 addition & 8 deletions jqm-all/jqm-api-client/jqm-api-client-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,5 @@
<version>${jakarta.xml.bind-api.version}</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>

</dependencies>
</project>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.enioka.jqm.client.api;

import java.util.Properties;

/**
* This interface must be implemented by all client providers. It simply defines the "create client" interface.
*
*/
public interface IClientFactory
{
/**
* Return the default client. Note this client is shared in the static context. (said otherwise: the same client is always returned
* inside a same class loading context). The initialization cost is only paid at first call.
*
* @return the default client
*/
JqmClient getClient(Properties properties);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package com.enioka.jqm.client.api;

import java.security.InvalidParameterException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.enioka.jqm.shared.exceptions.JqmMissingPluginException;
import com.enioka.jqm.shared.services.ServiceLoaderHelper;

public final class JqmClientFactory
{
protected static Logger jqmlogger = LoggerFactory.getLogger(JqmClientFactory.class);

private static Properties props = new Properties();
private static Map<Class<? extends IClientFactory>, IClientFactory> factories = new HashMap<>();

private static ConcurrentMap<String, JqmClient> clientCache = new ConcurrentHashMap<String, JqmClient>();
private static Map<Class<? extends IClientFactory>, JqmClient> defaultClientCache = new HashMap<>();

private static IClientFactory getFactoryFromServiceLoader(Class<? extends IClientFactory> clazz)
{
if (factories.containsKey(clazz))
{
return factories.get(clazz);
}

IClientFactory factory;
try
{
factory = ServiceLoaderHelper.getService(ServiceLoader.load(clazz));
factories.put(clazz, factory);
}
catch (JqmMissingPluginException e)
{
throw new JqmClientException("Could not find any JQM client provider", e);
}
return factory;
}

private JqmClientFactory()
{
// Utility class
}

/**
* Most client providers use a specific configuration file. However, it may be desired to overload these values with runtime values.
* This method enables a client to specify these values.<br>
* <strong>Note that the parameter names depend on the provider!</strong><br>
* Moreover, changing the properties only impact <code>JqmClient</code>s created after the modification. It is important to keep in mind
* that created <code>JqmClient</code>s are cached - therefore it is customary to use this function <strong>before creating any
* clients</strong>.
*
* @param properties
* a non null property bag
* @throws InvalidParameterException
* if props is null
*/
public static void setProperties(Properties properties)
{
if (props == null)
{
throw new InvalidParameterException("props cannot be null");
}
JqmClientFactory.props = properties;
}

/**
* See {@link #setProperties(Properties)}
*
* @param key
* a non null non empty parameter key.
* @param value
* value of the parameter.
*/
public static void setProperty(String key, Object value)
{
if (props == null)
{
throw new IllegalStateException("properties are null");
}
if (key == null || key.isEmpty())
{
throw new InvalidParameterException("key cannot be empty or null");
}
props.put(key, value);
}

public static void removeProperty(String key)
{
if (props == null)
{
return;
}
props.remove(key);
}

/**
* Return the default client. Note this client is shared in the static context. (said otherwise: the same client is always returned
* inside a same class loading context). The initialization cost is only paid at first call.
*
* @return the default client
*/
public static JqmClient getClient()
{
return getClient(null, null, true, IClientFactory.class);
}

/**
* Return the default client for a specific subtype of JQM client. Note this client is shared in the static context. (said otherwise:
* the same client is always returned inside a same class loading context). The initialization cost is only paid at first call.
*
* @return the default client for this client subtype.
*/
public static JqmClient getClient(Class<? extends IClientFactory> clazz)
{
return getClient(null, null, true, clazz);
}

/**
* Return a new client that may be cached or not. Given properties are always use when not cached, and only used at creation time for
* cached clients.
*
* @param name
* if null, default client. Otherwise, helpful to retrieve cached clients later.
* @param p
* a set of properties. Implementation specific. Unknown properties are silently ignored.
* @param cached
* if false, the client will not be cached and subsequent calls with the same name will return different objects.
*/
public static JqmClient getClient(String name, Properties p, boolean cached, Class<? extends IClientFactory> clazz)
{
Properties p2 = null;
if (p == null)
{
p2 = props;
}
else
{
p2 = new Properties(props);
p2.putAll(p);
}

var factory = getFactoryFromServiceLoader(clazz);

if (!cached)
{
return factory.getClient(p2);
}

synchronized (clientCache)
{
if (name == null)
{
if (defaultClientCache.get(clazz) == null)
{
jqmlogger.trace("creating default client");
defaultClientCache.put(clazz, factory.getClient(p2));
}
return defaultClientCache.get(clazz);
}
else
{
clientCache.putIfAbsent(name, factory.getClient(p2));
return clientCache.get(name);
}
}
}

/**
* Close and remove all cached clients.
*/
public static void reset()
{
synchronized (clientCache)
{
for (var c : clientCache.values())
{
c.dispose();
}
clientCache.clear();

for (var c : defaultClientCache.values())
{
c.dispose();
}
defaultClientCache.clear();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.enioka.jqm.client.api;

import java.util.Properties;

import com.enioka.jqm.client.shared.IDbClientFactory;

/**
* A specialized version of JqmClientFactory only using clients that connect to the database. Only useful if both clients are present on the
* class path, as in tests - otherwise use JqmClientFactory directly.
*/
public class JqmDbClientFactory
{
public static void setProperties(Properties properties)
{
JqmClientFactory.setProperties(properties);
}

public static void setProperty(String key, Object value)
{
JqmClientFactory.setProperty(key, value);
}

public static JqmClient getClient()
{
return JqmClientFactory.getClient(null, null, true, IDbClientFactory.class);
}

public static JqmClient getClient(String name, Properties p, boolean cached)
{
return JqmClientFactory.getClient(name, p, cached, IDbClientFactory.class);
}

public static void reset()
{
JqmClientFactory.reset();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.enioka.jqm.client.api;

import java.util.Properties;

import com.enioka.jqm.client.shared.IWsClientFactory;

/**
* A specialized version of JqmClientFactory only using clients that connect to the JQM web services. Only useful if both clients are
* present on the class path, as in tests - otherwise use JqmClientFactory directly.
*/
public class JqmWsClientFactory
{
public static void setProperties(Properties properties)
{
JqmClientFactory.setProperties(properties);
}

public static void setProperty(String key, Object value)
{
JqmClientFactory.setProperty(key, value);
}

public static JqmClient getClient()
{
return JqmClientFactory.getClient(null, null, true, IWsClientFactory.class);
}

public static JqmClient getClient(String name, Properties p, boolean cached)
{
return JqmClientFactory.getClient(name, p, cached, IWsClientFactory.class);
}

public static void reset()
{
JqmClientFactory.reset();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,4 @@
* JQM client API definition (a few interfaces and classes used by said interfaces). This is a public package. There are no implementations
* in this package.
*/
@org.osgi.annotation.bundle.Export
@org.osgi.annotation.versioning.Version("3.0.0")
package com.enioka.jqm.client.api;
Loading

0 comments on commit a78ad41

Please sign in to comment.