diff --git a/.gitignore b/.gitignore
index b3f19fd..4398718 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,3 +16,5 @@ hs_err_pid*
/.project
/doc/
/property-inject.launch
+
+/.idea
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index e75d688..61ff49b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -78,6 +78,12 @@
5.9.2
test
+
+ org.junit.platform
+ junit-platform-runner
+ 1.9.2
+ test
+
org.mockito
mockito-junit-jupiter
diff --git a/src/main/java/io/xlate/inject/PropertyFactory.java b/src/main/java/io/xlate/inject/PropertyFactory.java
index ac921a4..9cf74a1 100644
--- a/src/main/java/io/xlate/inject/PropertyFactory.java
+++ b/src/main/java/io/xlate/inject/PropertyFactory.java
@@ -21,10 +21,7 @@
import java.io.InputStream;
import java.lang.reflect.Executable;
import java.lang.reflect.Member;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URL;
-import java.net.URLStreamHandler;
+import java.net.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@@ -52,32 +49,46 @@ URLStreamHandler classPathHandler(Class> beanType) {
return new ClasspathURLStreamHandler(beanType.getClassLoader());
}
+ URL getResourceUrl(String filename) throws MalformedURLException {
+ return getResourceUrlByLocation(filename, this.getClass());
+ }
+
URL getResourceUrl(PropertyResource annotation, Class> beanType) throws MalformedURLException {
- final String location = annotation.value();
- final URL resourceUrl;
+ String location = annotation.value();
+
if (location.isEmpty()) {
- StringBuilder resourceName = new StringBuilder(CLASSPATH);
- resourceName.append(':');
- resourceName.append(beanType.getName().replace('.', '/'));
- resourceName.append(".properties");
- resourceUrl = new URL(null, resourceName.toString(), classPathHandler(beanType));
+ StringBuilder resourceName = new StringBuilder(CLASSPATH);
+ resourceName.append(':');
+ resourceName.append(beanType.getName().replace('.', '/'));
+ resourceName.append(".properties");
+ return new URL(null, resourceName.toString(), classPathHandler(beanType));
} else {
- final String resolvedLocation;
+ String resolvedLocation;
if (annotation.resolveEnvironment()) {
resolvedLocation = replaceEnvironmentReferences(location);
} else {
resolvedLocation = location;
}
+ return getResourceUrlByLocation(resolvedLocation, beanType);
+ }
+ }
+ private URL getResourceUrlByLocation(String location,Class> beanType) throws MalformedURLException {
+ URL resourceUrl;
try {
- final URI resourceId = URI.create(resolvedLocation);
+ final URI resourceId;
+ if (location.indexOf(':')>0 && !location.startsWith(CLASSPATH)) { // we assume it is an url
+ resourceId = new URL(location).toURI();
+ } else {
+ resourceId = URI.create(location);
+ }
final String scheme = resourceId.getScheme();
if (scheme != null) {
if (CLASSPATH.equals(scheme)) {
- resourceUrl = new URL(null, resolvedLocation, classPathHandler(beanType));
+ resourceUrl = new URL(null, location, classPathHandler(beanType));
} else {
resourceUrl = resourceId.toURL();
}
@@ -86,8 +97,10 @@ URL getResourceUrl(PropertyResource annotation, Class> beanType) throws Malfor
}
} catch (IllegalArgumentException | MalformedURLException e) {
throw new InjectionException(e);
+ } catch (URISyntaxException e) {
+ throw new InjectionException(e);
}
- }
+
return resourceUrl;
}
diff --git a/src/main/java/io/xlate/inject/PropertyFileProvider.java b/src/main/java/io/xlate/inject/PropertyFileProvider.java
new file mode 100644
index 0000000..0096a33
--- /dev/null
+++ b/src/main/java/io/xlate/inject/PropertyFileProvider.java
@@ -0,0 +1,7 @@
+package io.xlate.inject;
+
+public interface PropertyFileProvider {
+
+ String getLocation();
+
+}
diff --git a/src/main/java/io/xlate/inject/PropertyProducerBean.java b/src/main/java/io/xlate/inject/PropertyProducerBean.java
index cefa657..06e7acf 100644
--- a/src/main/java/io/xlate/inject/PropertyProducerBean.java
+++ b/src/main/java/io/xlate/inject/PropertyProducerBean.java
@@ -33,8 +33,10 @@
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.InjectionException;
+import javax.enterprise.inject.Instance;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
+import javax.inject.Inject;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
@@ -48,6 +50,9 @@ public class PropertyProducerBean {
private final PropertyFactory factory = new PropertyFactory();
+ @Inject
+ private Instance propertyFilenameProvider;
+
@Produces
@Dependent
@Property
@@ -219,15 +224,22 @@ String getProperty(InjectionPoint point) throws IOException {
return systemProperty;
}
+ final boolean hasGlobalFile = propertyFilenameProvider != null && propertyFilenameProvider.isResolvable();
+
final PropertyResource resource = annotation.resource();
- final String value;
- final URL resourceUrl = factory.getResourceUrl(resource, beanType);
- value = factory.getProperty(resourceUrl, resource.format(), resource.allowMissingResource(), propertyName, defaultValue);
+ String value;
+ URL resourceUrl = factory.getResourceUrl(resource, beanType);
+ value = factory.getProperty(resourceUrl, resource.format(), hasGlobalFile ? true : resource.allowMissingResource(), propertyName, defaultValue);
if (value != null && annotation.resolveEnvironment()) {
return factory.replaceEnvironmentReferences(value);
}
+ if (value == null && hasGlobalFile){
+ resourceUrl = factory.getResourceUrl(propertyFilenameProvider.get().getLocation());
+ value = factory.getProperty(resourceUrl, resource.format(), resource.allowMissingResource(), propertyName, defaultValue);
+ }
+
return value;
}
}
diff --git a/src/main/java/io/xlate/inject/PropertyResourceProducerBean.java b/src/main/java/io/xlate/inject/PropertyResourceProducerBean.java
index 8d18832..deaf2d4 100644
--- a/src/main/java/io/xlate/inject/PropertyResourceProducerBean.java
+++ b/src/main/java/io/xlate/inject/PropertyResourceProducerBean.java
@@ -23,9 +23,11 @@
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.InjectionException;
+import javax.enterprise.inject.Instance;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.Annotated;
import javax.enterprise.inject.spi.InjectionPoint;
+import javax.inject.Inject;
@ApplicationScoped
public class PropertyResourceProducerBean {
@@ -35,6 +37,9 @@ public class PropertyResourceProducerBean {
private final PropertyFactory factory = new PropertyFactory();
+ @Inject
+ private Instance propertyFilenameProvider;
+
@Produces
@Dependent
@PropertyResource
@@ -48,13 +53,22 @@ public Properties produceProperties(InjectionPoint point) {
final Class> beanType = point.getMember().getDeclaringClass();
final PropertyResource annotation = annotated.getAnnotation(PropertyResource.class);
final PropertyResourceFormat format = annotation.format();
- URL resourceUrl = null;
+ Properties p = new Properties();
try {
+ URL resourceUrl = null;
+ boolean hasGlobalPropertyFile =propertyFilenameProvider != null && propertyFilenameProvider.isResolvable();
+ if (hasGlobalPropertyFile) {
+ String globalFile = propertyFilenameProvider.get().getLocation();
+ resourceUrl = factory.getResourceUrl(globalFile);
+ p.putAll(factory.getProperties(resourceUrl, format, annotation.allowMissingResource()));
+ }
resourceUrl = factory.getResourceUrl(annotation, beanType);
- return factory.getProperties(resourceUrl, format, annotation.allowMissingResource());
+ p.putAll(factory.getProperties(resourceUrl, format, hasGlobalPropertyFile ? true: annotation.allowMissingResource()));
+ return p;
} catch (Exception e) {
throw new InjectionException(e);
}
}
+
}
diff --git a/src/test/java/io/xlate/inject/PropertyProducerBeanIT.java b/src/test/java/io/xlate/inject/PropertyProducerBeanIT.java
index 0c4c2c3..aef44a4 100644
--- a/src/test/java/io/xlate/inject/PropertyProducerBeanIT.java
+++ b/src/test/java/io/xlate/inject/PropertyProducerBeanIT.java
@@ -24,6 +24,7 @@
import org.jboss.weld.junit5.WeldInitiator;
import org.jboss.weld.junit5.WeldJunit5Extension;
import org.jboss.weld.junit5.WeldSetup;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.platform.runner.JUnitPlatform;
@@ -35,7 +36,7 @@ class PropertyProducerBeanIT {
@WeldSetup
public WeldInitiator weld = WeldInitiator
- .from(PropertyProducerBean.class)
+ .from(PropertyProducerBean.class, TestFileProvider.class)
.build();
@Inject @Property
@@ -78,6 +79,10 @@ class PropertyProducerBeanIT {
@Property
int int3;
+ @BeforeAll
+ public static void setUp() {
+ System.setProperty("string6.property.name", "string6value.system");
+ }
@Test
void testString1_DefaultLookup() {
assertEquals("string1value", string1);
diff --git a/src/test/java/io/xlate/inject/PropertyProducerBeanTest.java b/src/test/java/io/xlate/inject/PropertyProducerBeanTest.java
index 5373d4a..1224add 100644
--- a/src/test/java/io/xlate/inject/PropertyProducerBeanTest.java
+++ b/src/test/java/io/xlate/inject/PropertyProducerBeanTest.java
@@ -31,6 +31,7 @@
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
@@ -41,6 +42,7 @@
import javax.enterprise.inject.spi.InjectionPoint;
import javax.json.Json;
+import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -61,11 +63,14 @@ class PropertyProducerBeanTest {
@Mock
PropertyResource defaultPropertyResource;
+ private Locale locale;
@BeforeEach
void setup() {
bean = new PropertyProducerBean();
when(defaultPropertyResource.value()).thenReturn("");
when(defaultPropertyResource.format()).thenReturn(PropertyResourceFormat.PROPERTIES);
+ locale = Locale.getDefault();
+ Locale.setDefault(Locale.ENGLISH);
}
private Property mockProperty(String name,
@@ -959,4 +964,8 @@ void testProducePropertyJsonObjectInvalid() {
});
}
+ @AfterEach
+ public void teardown(){
+ Locale.setDefault(locale);
+ }
}
diff --git a/src/test/java/io/xlate/inject/PropertyResourceProducerBeanIT.java b/src/test/java/io/xlate/inject/PropertyResourceProducerBeanIT.java
index 0ad61f0..4eafbb6 100644
--- a/src/test/java/io/xlate/inject/PropertyResourceProducerBeanIT.java
+++ b/src/test/java/io/xlate/inject/PropertyResourceProducerBeanIT.java
@@ -40,6 +40,7 @@ class PropertyResourceProducerBeanIT {
.from(PropertyResourceProducerBean.class)
.build();
+
@Inject
@PropertyResource
Properties defaultProps;
@@ -62,4 +63,5 @@ void testProps2() {
assertEquals(1, props2.size());
assertEquals("true", props2.getProperty("value.is.found"));
}
+
}
diff --git a/src/test/java/io/xlate/inject/PropertyResourceProducerBeanTest.java b/src/test/java/io/xlate/inject/PropertyResourceProducerBeanTest.java
index 6c7fa38..4be0f83 100644
--- a/src/test/java/io/xlate/inject/PropertyResourceProducerBeanTest.java
+++ b/src/test/java/io/xlate/inject/PropertyResourceProducerBeanTest.java
@@ -31,7 +31,10 @@
import javax.enterprise.inject.spi.InjectionPoint;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledOnOs;
+import org.junit.jupiter.api.condition.OS;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;
@@ -140,6 +143,7 @@ void testEnvironmentVarForResourceLocation() {
}
@Test
+ @DisabledOnOs(OS.WINDOWS)
void testProducePropertiesUnreadable() {
File resource = new File("target/test-classes/io/xlate/inject/Unreadable.properties");
resource.setReadable(false);
diff --git a/src/test/java/io/xlate/inject/PropertyResourceProducerBeanWithConfigurationFile.java b/src/test/java/io/xlate/inject/PropertyResourceProducerBeanWithConfigurationFile.java
new file mode 100644
index 0000000..f6f97f8
--- /dev/null
+++ b/src/test/java/io/xlate/inject/PropertyResourceProducerBeanWithConfigurationFile.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (C) 2018 xlate.io LLC, http://www.xlate.io
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ ******************************************************************************/
+package io.xlate.inject;
+
+import org.jboss.weld.junit5.WeldInitiator;
+import org.jboss.weld.junit5.WeldJunit5Extension;
+import org.jboss.weld.junit5.WeldSetup;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.platform.runner.JUnitPlatform;
+import org.junit.runner.RunWith;
+
+import javax.inject.Inject;
+import java.util.Properties;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@RunWith(JUnitPlatform.class)
+@ExtendWith(WeldJunit5Extension.class)
+class PropertyResourceProducerBeanWithConfigurationFile {
+
+ @WeldSetup
+ WeldInitiator weld = WeldInitiator
+ .from(PropertyResourceProducerBean.class, TestFileProvider.class)
+ .build();
+
+
+ @Inject
+ @PropertyResource
+ Properties defaultProps;
+
+
+ @Inject
+ @PropertyResource("io/xlate/inject/PropertyResourceProducerBeanIT2.properties")
+ Properties props2;
+
+ @Test
+ void testGlobalFile() {
+ WeldInitiator weld = WeldInitiator
+ .from(PropertyResourceProducerBean.class, TestFileProvider.class)
+ .build();
+ assertNotNull(defaultProps);
+ System.out.println(defaultProps);
+ assertEquals(3, defaultProps.size());
+ assertEquals("x", defaultProps.getProperty("key1"));
+ assertEquals("y", defaultProps.getProperty("key2"));
+ assertEquals("false", defaultProps.getProperty("value.is.found"));
+ }
+
+ @Test
+ void testGlobalFileOverriddenByLocalLocation() {
+ WeldInitiator weld = WeldInitiator
+ .from(PropertyResourceProducerBean.class, TestFileProvider.class)
+ .build();
+ assertNotNull(props2);
+ System.out.println(props2);
+ assertEquals(3, props2.size());
+ assertEquals("true", props2.getProperty("value.is.found"));
+ assertEquals("x", props2.getProperty("key1"));
+ assertEquals("y", props2.getProperty("key2"));
+ }
+
+
+}
diff --git a/src/test/java/io/xlate/inject/TestFileProvider.java b/src/test/java/io/xlate/inject/TestFileProvider.java
new file mode 100644
index 0000000..5aaa13b
--- /dev/null
+++ b/src/test/java/io/xlate/inject/TestFileProvider.java
@@ -0,0 +1,15 @@
+package io.xlate.inject;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.Default;
+
+@ApplicationScoped
+@Default
+public class TestFileProvider implements PropertyFileProvider {
+
+ @Override
+ public String getLocation() {
+ return "classpath:global.properties";
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/resources/global.properties b/src/test/resources/global.properties
new file mode 100644
index 0000000..cfae97b
--- /dev/null
+++ b/src/test/resources/global.properties
@@ -0,0 +1,3 @@
+key1=x
+key2=y
+value.is.found=false
\ No newline at end of file
diff --git a/src/test/resources/io/xlate/inject/PropertyResourceProducerBeanWithGlobalConfiguration.properties b/src/test/resources/io/xlate/inject/PropertyResourceProducerBeanWithGlobalConfiguration.properties
new file mode 100644
index 0000000..de075e5
--- /dev/null
+++ b/src/test/resources/io/xlate/inject/PropertyResourceProducerBeanWithGlobalConfiguration.properties
@@ -0,0 +1 @@
+value.is.found=in bean type based config file