Skip to content

Commit

Permalink
MicroprofileConfig SPI API for Jersey
Browse files Browse the repository at this point in the history
Signed-off-by: Maxim Nesen <maxim.nesen@oracle.com>
  • Loading branch information
senivam committed Apr 25, 2019
1 parent 9eaa859 commit 1c4967c
Show file tree
Hide file tree
Showing 31 changed files with 1,512 additions and 0 deletions.
5 changes: 5 additions & 0 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@
<artifactId>jersey-metainf-services</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-mp-config</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-mvc</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,17 @@ public final class CommonProperties {
*/
public static final String OUTBOUND_CONTENT_LENGTH_BUFFER_SERVER = "jersey.config.server.contentLength.buffer";

/**
* Property which allows (if true) default System properties configuration provider.
*
* Effective if there are no any external properties providers
*
* Shall be set (if used) in system properties.
* @since 2.9
*/

public static final String ALLOW_SYSTEM_PROPERTIES_PROVIDER = "jersey.config.allowSystemPropertiesProvider";

/**
* Prevent instantiation.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.internal.config;

import org.glassfish.jersey.internal.ServiceFinder;
import org.glassfish.jersey.spi.ExternalConfigurationModel;
import org.glassfish.jersey.spi.ExternalConfigurationProvider;

import javax.annotation.Priority;
import javax.ws.rs.core.Configurable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

/**
* Factory for external properties providers
* Offers methods to work with properties loaded from providers or
* just configure Jersey's Configurables with loaded properties from providers
*/
public class ExternalPropertiesConfigurationFactory {

private ExternalPropertiesConfigurationFactory() {
}

private static final ExternalPropertiesConfigurationFactory factory = new ExternalPropertiesConfigurationFactory();

public static ExternalPropertiesConfigurationFactory getFactory() {
return factory;
}

/**
* Map of merged properties from all found providers
*
* @return map of merged properties from all found/plugged providers
*/
public Map<String, Object> readExternalPropertiesMap() {

final ExternalConfigurationProvider provider = mergeConfigs(getExternalConfigurations());
return provider == null ? new HashMap<>() : provider.getProperties();
}


/**
* Input Configurable object shall be provided in order to be filled with all found properties
*
* @param config Input Configurable initialised object to be filled with properties
*/
public void confiure(Configurable config) {

if (config instanceof ExternalConfigurationModel) {
throw new IllegalArgumentException("Config shall not be from external properties");
}

final Map<String, Object> properties = readExternalPropertiesMap();
properties.forEach((k, v) -> config.property(k, v));

}

/**
* Merged config model from all found configuration models
*
* @return merged Model object with all properties
*/
public ExternalConfigurationModel getConfig() {
final ExternalConfigurationProvider provider = mergeConfigs(getExternalConfigurations());
return provider == null ? null : provider.getConfiguration();
}

/**
* List of all found models as they are found by Jersey
*
* @return list of models (or empty list)
*/
public List<ExternalConfigurationProvider> getExternalConfigurations() {
final List<ExternalConfigurationProvider> providers = new ArrayList<>();
final ServiceFinder<ExternalConfigurationProvider> finder =
ServiceFinder.find(ExternalConfigurationProvider.class);
if (finder.iterator().hasNext()) {
finder.forEach(providers::add);
} else {
providers.add(new SystemPropertiesConfigurationProvider());
}
return providers;
}

private ExternalConfigurationProvider mergeConfigs(List<ExternalConfigurationProvider> configurations) {
final Set<ExternalConfigurationProvider> orderedConfigurations = orderConfigs(configurations);
final Iterator<ExternalConfigurationProvider> configurationIterator = orderedConfigurations.iterator();
if (!configurationIterator.hasNext()) {
return null;
}
final ExternalConfigurationProvider firstConfig = configurationIterator.next();
while (configurationIterator.hasNext()) {
final ExternalConfigurationProvider nextConfig = configurationIterator.next();
firstConfig.merge(nextConfig.getConfiguration());
}

return firstConfig;
}

private Set<ExternalConfigurationProvider> orderConfigs(List<ExternalConfigurationProvider> configurations) {

final SortedSet<ExternalConfigurationProvider> sortedSet = new TreeSet<>((config1, config2) -> {

if (
config1.getClass().isAnnotationPresent(Priority.class)
&& config2.getClass().isAnnotationPresent(Priority.class)
) {
int priority1 = config1.getClass().getAnnotation(Priority.class).value();
int priority2 = config2.getClass().getAnnotation(Priority.class).value();
if (priority1 == priority2) {
return config1.getClass().getName().compareTo(config2.getClass().getName());
}
return Integer.compare(priority1, priority2);
}
return config1.getClass().getName().compareTo(config2.getClass().getName());
});
sortedSet.addAll(configurations);
return Collections.unmodifiableSortedSet(sortedSet);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
/*
* Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.internal.config;

import org.glassfish.jersey.CommonProperties;
import org.glassfish.jersey.internal.util.PropertiesHelper;
import org.glassfish.jersey.internal.util.ReflectionHelper;
import org.glassfish.jersey.spi.ExternalConfigurationModel;

import javax.ws.rs.RuntimeType;
import javax.ws.rs.core.Feature;
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.logging.Logger;


public class SystemPropertiesConfigurationModel implements ExternalConfigurationModel<Void> {

private static final Logger log = Logger.getLogger(SystemPropertiesConfigurationModel.class.getName());


private static final Map<Class, Function> converters = new HashMap<>();
static {
converters.put(String.class, (Function<String, String>) s -> s);
converters.put(Integer.class, (Function<String, Integer>) s -> Integer.valueOf(s));
converters.put(Boolean.class, (Function<String, Boolean>) s -> s.equalsIgnoreCase("1")
? true
: Boolean.parseBoolean(s));
}

private String getSystemProperty(String name) {
return AccessController.doPrivileged(PropertiesHelper.getSystemProperty(name));
}

@Override
public <T> T as(String name, Class<T> clazz) {
if (converters.get(clazz) == null) {
throw new IllegalArgumentException("Unsupported class type");
}
return (name != null && clazz != null && isProperty(name))
? clazz.cast(converters.get(clazz).apply(getSystemProperty(name)))
: null;
}



@Override
public <T> Optional<T> getOptionalProperty(String name, Class<T> clazz) {
return Optional.of(as(name, clazz));
}

@Override
public ExternalConfigurationModel mergeProperties(Map<String, Object> inputProperties) {
return this;
}

@Override
public Void getConfig() {
return null;
}

@Override
public boolean isProperty(String name) {
return Optional.ofNullable(
AccessController.doPrivileged(
PropertiesHelper.getSystemProperty(name)
)
).isPresent();
}

@Override
public RuntimeType getRuntimeType() {
return null;
}

@Override
public Map<String, Object> getProperties() {
final Map<String, Object> result = new HashMap<>();

final Boolean allowSystemPropertiesProvider = as(
CommonProperties.ALLOW_SYSTEM_PROPERTIES_PROVIDER, Boolean.class
);
if (!Boolean.TRUE.equals(allowSystemPropertiesProvider)) {
log.finer("System properties configuration provider not allowed");
return result;
}

try {
AccessController.doPrivileged(PropertiesHelper.getSystemProperties())
.forEach((k, v) -> result.put(String.valueOf(k), v));
} catch (SecurityException se) {
log.warning(se.getLocalizedMessage());
return getExpectedSystemProperties();
}
return result;
}

private Map<String, Object> getExpectedSystemProperties() {


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

mapFieldsToProperties(result, CommonProperties.class);



mapFieldsToProperties(result,
AccessController.doPrivileged(
ReflectionHelper.classForNamePA("org.glassfish.jersey.server.ServerProperties")
)
);

mapFieldsToProperties(result,
AccessController.doPrivileged(
ReflectionHelper.classForNamePA("org.glassfish.jersey.client.ClientProperties")
)
);


return result;
}

private <T> void mapFieldsToProperties(Map<String, Object> properties, Class<T> clazz) {
if (clazz == null) {
return;
}

final Field[] fields = AccessController.doPrivileged(
ReflectionHelper.getDeclaredFieldsPA(clazz)
);

for (final Field field : fields) {
if (field.getType().isAssignableFrom(String.class)) {
final String propertyValue = getPropertyNameByField(field);
properties.put(propertyValue, PropertiesHelper.getSystemProperty(propertyValue));
}
}
}

private String getPropertyNameByField(Field field) {
return AccessController.doPrivileged((PrivilegedAction<String>) () -> {
try {
return (String) field.get(null);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
});
}

@Override
public Object getProperty(String name) {
return getSystemProperty(name);
}

@Override
public Collection<String> getPropertyNames() {
return PropertiesHelper.getSystemProperties().run().stringPropertyNames();
}

@Override
public boolean isEnabled(Feature feature) {
return false;
}

@Override
public boolean isEnabled(Class<? extends Feature> featureClass) {
return false;
}

@Override
public boolean isRegistered(Object component) {
return false;
}

@Override
public boolean isRegistered(Class<?> componentClass) {
return false;
}

@Override
public Map<Class<?>, Integer> getContracts(Class<?> componentClass) {
return null;
}

@Override
public Set<Class<?>> getClasses() {
return null;
}

@Override
public Set<Object> getInstances() {
return null;
}
}
Loading

0 comments on commit 1c4967c

Please sign in to comment.