Skip to content

Commit

Permalink
feat: spring application can now configure ap4k via application.proep…
Browse files Browse the repository at this point in the history
…erties.
  • Loading branch information
iocanel authored and metacosm committed Jun 12, 2019
1 parent 5696244 commit d4347f4
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 11 deletions.
@@ -1,12 +1,18 @@
package io.ap4k.kubernetes.generator;

import io.ap4k.Generator;
import io.ap4k.kubernetes.annotation.KubernetesApplication;

import java.util.Collections;
import java.util.List;

public class DefaultKubernetesApplicationGenerator implements KubernetesApplicationGenerator {

public DefaultKubernetesApplicationGenerator () {
Generator.registerAnnotationClass(KUBERNETES, KubernetesApplication.class);
Generator.registerGenerator(KUBERNETES, this);
}

@Override
public List<Class> getSupportedAnnotations() {
return Collections.singletonList(KubernetesApplication.class);
Expand Down
Expand Up @@ -48,6 +48,7 @@ public interface KubernetesApplicationGenerator extends Generator, SessionListen

String KUBERNETES = "kubernetes";

@Override
default void add(Map map) {
add(new ConfigurationSupplier<>(
KubernetesConfigAdapter
Expand Down
@@ -1,12 +1,18 @@
package io.ap4k.openshift.generator;

import io.ap4k.Generator;
import io.ap4k.openshift.annotation.OpenshiftApplication;

import java.util.Collections;
import java.util.List;

public class DefaultOpenshiftApplicationGenerator implements OpenshiftApplicationGenerator {

public DefaultOpenshiftApplicationGenerator () {
Generator.registerAnnotationClass(OPENSHIFT, OpenshiftApplication.class);
Generator.registerGenerator(OPENSHIFT, this);
}

@Override
public List<Class> getSupportedAnnotations() {
return Collections.singletonList(OpenshiftApplication.class);
Expand Down
Expand Up @@ -39,6 +39,7 @@

public interface OpenshiftApplicationGenerator extends Generator, WithSession, WithProject, SessionListener {

String OPENSHIFT = "openshift";
String DEFAULT_S2I_BUILDER_IMAGE = "fabric8/s2i-java:2.3";

OpenshiftConfig DEFAULT_SOURCE_TO_IMAGE_CONFIG = new OpenshiftConfigBuilder()
Expand Down
2 changes: 0 additions & 2 deletions core/src/main/java/io/ap4k/Configurators.java
Expand Up @@ -22,12 +22,10 @@

import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down
97 changes: 90 additions & 7 deletions core/src/main/java/io/ap4k/Generator.java
Expand Up @@ -16,30 +16,113 @@
**/
package io.ap4k;


import java.lang.annotation.Annotation;
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.ServiceLoader;

/**
* Generates resources, based on the detected annotations.
*/
public interface Generator extends SessionHandler {

Map<String, Generator> GENERATORS = new HashMap<>();
Map<String, Class<? extends Annotation>> ANNOTATIONS = new HashMap<>();

/**
* @return all registered @{link Generator}s.
*/
static List<Generator> getGenerators() {
List<Generator> generators = new ArrayList<>();
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(Generator.class.getClassLoader());
ServiceLoader<Generator> loader = ServiceLoader.load(Generator.class);
Iterator<Generator> iterator = loader.iterator();
while (iterator.hasNext()) {
generators.add(iterator.next());
}
} finally {
Thread.currentThread().setContextClassLoader(tccl);
return generators;
}
}

/**
* Initialize
*/
static void init(Map<String, Object> map) {
// Lazy load generators
List<Generator> generators = getGenerators();
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
Generator generator = getGenerator(key);
if (value instanceof Map && generator != null) {
Map<String, Object> generatorMap = new HashMap<>();
Class annotationClass = ANNOTATIONS.get(key);
String newKey = annotationClass.getName();
generatorMap.put(newKey, value);
generator.add(generatorMap);
}
}
}

static Class<? extends Annotation> registerAnnotationClass(String key, Class<? extends Annotation> type) {
return ANNOTATIONS.put(key, type);
}

static Class<? extends Annotation> getAnnotationClass(String key) {
return ANNOTATIONS.get(key);
}

static Generator registerGenerator(String key, Generator generator) {
return GENERATORS.put(key, generator);
}

static Generator getGenerator(String key) {
return GENERATORS.get(key);
}

/**
* Generate the resources.
* This method may be called multiple times, but should only generate the resources once.
* Add a map as the generator input.
*/
void add(Map map);

/**
* Generate the resources. This method may be called multiple times, but should
* only generate the resources once.
*/
default void generate() {
//do nothing
// do nothing
session.close();
}

/**
* Returns a list of the annotations that are supported by the generator
* This is meant to be used by tools other than APT
* When the generator is extended by an APT processor, this method is never consulted
* Returns a list of the annotations that are supported by the generator This is
* meant to be used by tools other than APT When the generator is extended by an
* APT processor, this method is never consulted
*/
default List<Class> getSupportedAnnotations() {
return Collections.emptyList();
}

default Map<String, Object> filter(Map<String, Object> properties) {
Map<String, Object> result = new HashMap<>();
for (Map.Entry<String, Object> entry : properties.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (ANNOTATIONS.containsKey(key)) {
result.put(ANNOTATIONS.get(key).getName(), value);
} else {
result.put(key, value);
}
}
return result;
}

}
91 changes: 91 additions & 0 deletions core/src/main/java/io/ap4k/utils/Maps.java
@@ -0,0 +1,91 @@
/**
* Copyright 2018 The original authors.
*
* 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.ap4k.utils;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Pattern;

import io.ap4k.Ap4kException;

public class Maps {

public static Map<String, Object> fromProperties(InputStream is) {
Map<String, Object> result = new HashMap<>();
Properties properties = new Properties();
try {
properties.load(is);
} catch (IOException e) {
throw Ap4kException.launderThrowable(e);
}
for (Object key : properties.keySet()) {
String k = String.valueOf(key);
Object value = properties.get(key);
Map<String, Object> kv = asMap(k.split(Pattern.quote(".")), value);
merge(result, kv);
}
return result;
}

/**
* Convert a multipart-key value pair to a Map.
*/
private static Map<String, Object> asMap(String[] keys, Object value) {
if (keys == null || keys.length == 0) {
return null;
}

Map<String, Object> result = new HashMap<>();
if (keys.length == 1) {
result.put(keys[0], value);
return result;
}

String key = keys[0];
String[] remaining = new String[keys.length - 1];
System.arraycopy(keys, 1, remaining, 0, remaining.length);
Object nested = asMap(remaining, value);
result.put(key, nested);
return result;
}


/**
* Merge a nested map to an existing one.
* @param An existing map.
* @param The map that will be merged into the existing.
*/
private static void merge(Map<String, Object> existing, Map<String, Object> map) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();

Object existingValue = existing.get(key);
if (existingValue == null) {
existing.put(key, value);
} else if (existingValue instanceof Map && value instanceof Map) {
merge((Map<String, Object>) existingValue, (Map<String, Object>) value);
} else {
existing.put(key, value);
}
}
}
}
Expand Up @@ -16,17 +16,27 @@
**/
package io.ap4k.spring.apt;

import io.ap4k.Ap4kException;
import io.ap4k.Generator;
import io.ap4k.Session;
import io.ap4k.processor.AbstractAnnotationProcessor;
import io.ap4k.doc.Description;
import io.ap4k.spring.generator.SpringBootApplicationGenerator;
import io.ap4k.utils.Maps;

import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;

import javax.annotation.processing.Filer;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.TypeElement;
import javax.tools.FileObject;
import javax.tools.StandardLocation;

import java.io.FileNotFoundException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Description("Detects Spring Boot and set the runtime attribute to Spring Boot.")
Expand All @@ -42,7 +52,24 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
return true;
}

Generator.init(readApplicationProperties());
add(SPRING_BOOT_APPLICATION);
return false;
}

/**
* @return the application properties
*/
Map<String, Object> readApplicationProperties() {
Map<String, Object> result = new HashMap<>();
Filer filer = this.processingEnv.getFiler();
try {
FileObject f = filer.getResource(StandardLocation.CLASS_OUTPUT, "", "application.properties");
return Maps.fromProperties(f.openInputStream());
} catch (FileNotFoundException e) {
return Collections.emptyMap();
} catch (Exception e) {
throw Ap4kException.launderThrowable(e);
}
}
}
Expand Up @@ -38,7 +38,6 @@ public interface SpringBootApplicationGenerator extends Generator, WithSession {

Map<String, Object> SPRING_BOOT_APPLICATION = Collections.emptyMap();


@Override
default void add(Map map) {
session.configurators().add(new ConfigurationSupplier(new SpringApplicationConfigBuilder()));
Expand Down

0 comments on commit d4347f4

Please sign in to comment.