13 changed files with 15 additions and 1095 deletions
@ -1,126 +0,0 @@
@@ -1,126 +0,0 @@
|
||||
/* |
||||
* Copyright 2012 the original author or 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 org.springframework.boot.yaml; |
||||
|
||||
import java.util.LinkedHashMap; |
||||
import java.util.Map; |
||||
import java.util.Map.Entry; |
||||
import java.util.Properties; |
||||
|
||||
import org.springframework.beans.factory.FactoryBean; |
||||
|
||||
/** |
||||
* Factory for Map that reads from a YAML source. YAML is a nice human-readable format for |
||||
* configuration, and it has some useful hierarchical properties. It's more or less a |
||||
* superset of JSON, so it has a lot of similar features. If multiple resources are |
||||
* provided the later ones will override entries in the earlier ones hierarchically - that |
||||
* is all entries with the same nested key of type Map at any depth are merged. For |
||||
* example: |
||||
* |
||||
* <pre class="code"> |
||||
* foo: |
||||
* bar: |
||||
* one: two |
||||
* three: four |
||||
* |
||||
* </pre> |
||||
* |
||||
* plus (later in the list) |
||||
* |
||||
* <pre class="code"> |
||||
* foo: |
||||
* bar: |
||||
* one: 2 |
||||
* five: six |
||||
* |
||||
* </pre> |
||||
* |
||||
* results in an effecive input of |
||||
* |
||||
* <pre class="code"> |
||||
* foo: |
||||
* bar: |
||||
* one: 2 |
||||
* three: four |
||||
* five: six |
||||
* |
||||
* </pre> |
||||
* |
||||
* Note that the value of "foo" in the first document is not simply replaced with the |
||||
* value in the second, but its nested values are merged. |
||||
* |
||||
* @author Dave Syer |
||||
*/ |
||||
public class YamlMapFactoryBean extends YamlProcessor implements |
||||
FactoryBean<Map<String, Object>> { |
||||
|
||||
private boolean singleton = true; |
||||
|
||||
private Map<String, Object> instance; |
||||
|
||||
@Override |
||||
public Map<String, Object> getObject() { |
||||
if (!this.singleton || this.instance == null) { |
||||
final Map<String, Object> result = new LinkedHashMap<String, Object>(); |
||||
process(new MatchCallback() { |
||||
@Override |
||||
public void process(Properties properties, Map<String, Object> map) { |
||||
merge(result, map); |
||||
} |
||||
}); |
||||
this.instance = result; |
||||
} |
||||
return this.instance; |
||||
} |
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" }) |
||||
private void merge(Map<String, Object> output, Map<String, Object> map) { |
||||
for (Entry<String, Object> entry : map.entrySet()) { |
||||
String key = entry.getKey(); |
||||
Object value = entry.getValue(); |
||||
Object existing = output.get(key); |
||||
if (value instanceof Map && existing instanceof Map) { |
||||
Map<String, Object> result = new LinkedHashMap<String, Object>( |
||||
(Map) existing); |
||||
merge(result, (Map) value); |
||||
output.put(key, result); |
||||
} |
||||
else { |
||||
output.put(key, value); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Class<?> getObjectType() { |
||||
return Map.class; |
||||
} |
||||
|
||||
/** |
||||
* Set if a singleton should be created, or a new object on each request otherwise. |
||||
* Default is <code>true</code> (a singleton). |
||||
*/ |
||||
public void setSingleton(boolean singleton) { |
||||
this.singleton = singleton; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isSingleton() { |
||||
return this.singleton; |
||||
} |
||||
|
||||
} |
||||
@ -1,352 +0,0 @@
@@ -1,352 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2014 the original author or 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 org.springframework.boot.yaml; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.Collection; |
||||
import java.util.Collections; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Map.Entry; |
||||
import java.util.Properties; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.StringUtils; |
||||
import org.yaml.snakeyaml.Yaml; |
||||
|
||||
/** |
||||
* Base class for Yaml factories. |
||||
* |
||||
* @author Dave Syer |
||||
*/ |
||||
public abstract class YamlProcessor { |
||||
|
||||
private final Log logger = LogFactory.getLog(getClass()); |
||||
|
||||
private ResolutionMethod resolutionMethod = ResolutionMethod.OVERRIDE; |
||||
|
||||
private Resource[] resources = new Resource[0]; |
||||
|
||||
private List<DocumentMatcher> documentMatchers = Collections.emptyList(); |
||||
|
||||
private boolean matchDefault = true; |
||||
|
||||
/** |
||||
* A map of document matchers allowing callers to selectively use only some of the |
||||
* documents in a YAML resource. In YAML documents are separated by <code>---</code> |
||||
* lines, and each document is converted to properties before the match is made. E.g. |
||||
* |
||||
* <pre class="code"> |
||||
* environment: dev |
||||
* url: http://dev.bar.com
|
||||
* name: Developer Setup |
||||
* --- |
||||
* environment: prod |
||||
* url:http://foo.bar.com
|
||||
* name: My Cool App |
||||
* </pre> |
||||
* |
||||
* when mapped with |
||||
* <code>documentMatchers = YamlProcessor.mapMatcher({"environment": "prod"})</code> |
||||
* would end up as |
||||
* |
||||
* <pre class="code"> |
||||
* environment=prod |
||||
* url=http://foo.bar.com
|
||||
* name=My Cool App |
||||
* url=http://dev.bar.com
|
||||
* </pre> |
||||
* @param matchers a map of keys to value patterns (regular expressions) |
||||
*/ |
||||
public void setDocumentMatchers(DocumentMatcher... matchers) { |
||||
this.documentMatchers = Collections |
||||
.unmodifiableList(new ArrayList<DocumentMatcher>(Arrays.asList(matchers))); |
||||
} |
||||
|
||||
/** |
||||
* Flag indicating that a document for which all the |
||||
* {@link #setDocumentMatchers(DocumentMatcher...) document matchers} abstain will |
||||
* nevertheless match. |
||||
* @param matchDefault the flag to set (default true) |
||||
*/ |
||||
public void setMatchDefault(boolean matchDefault) { |
||||
this.matchDefault = matchDefault; |
||||
} |
||||
|
||||
/** |
||||
* Method to use for resolving resources. Each resource will be converted to a Map, so |
||||
* this property is used to decide which map entries to keep in the final output from |
||||
* this factory. |
||||
* @param resolutionMethod the resolution method to set (defaults to |
||||
* {@link ResolutionMethod#OVERRIDE}). |
||||
*/ |
||||
public void setResolutionMethod(ResolutionMethod resolutionMethod) { |
||||
Assert.notNull(resolutionMethod, "ResolutionMethod must not be null"); |
||||
this.resolutionMethod = resolutionMethod; |
||||
} |
||||
|
||||
/** |
||||
* @param resources the resources to set |
||||
*/ |
||||
public void setResources(Resource[] resources) { |
||||
this.resources = (resources == null ? null : resources.clone()); |
||||
} |
||||
|
||||
/** |
||||
* Provides an opportunity for subclasses to process the Yaml parsed from the supplied |
||||
* resources. Each resource is parsed in turn and the documents inside checked against |
||||
* the {@link #setDocumentMatchers(DocumentMatcher...) matchers}. If a document |
||||
* matches it is passed into the callback, along with its representation as |
||||
* Properties. Depending on the {@link #setResolutionMethod(ResolutionMethod)} not all |
||||
* of the documents will be parsed. |
||||
* @param callback a callback to delegate to once matching documents are found |
||||
*/ |
||||
protected void process(MatchCallback callback) { |
||||
Yaml yaml = new Yaml(); |
||||
for (Resource resource : this.resources) { |
||||
boolean found = process(callback, yaml, resource); |
||||
if (this.resolutionMethod == ResolutionMethod.FIRST_FOUND && found) { |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
|
||||
private boolean process(MatchCallback callback, Yaml yaml, Resource resource) { |
||||
int count = 0; |
||||
try { |
||||
if (this.logger.isDebugEnabled()) { |
||||
this.logger.debug("Loading from YAML: " + resource); |
||||
} |
||||
for (Object object : yaml.loadAll(resource.getInputStream())) { |
||||
if (object != null && process(asMap(object), callback)) { |
||||
count++; |
||||
if (this.resolutionMethod == ResolutionMethod.FIRST_FOUND) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
if (this.logger.isDebugEnabled()) { |
||||
this.logger.debug("Loaded " + count + " document" |
||||
+ (count > 1 ? "s" : "") + " from YAML resource: " + resource); |
||||
} |
||||
} |
||||
catch (IOException ex) { |
||||
handleProcessError(resource, ex); |
||||
} |
||||
return count > 0; |
||||
} |
||||
|
||||
private void handleProcessError(Resource resource, IOException ex) { |
||||
if (this.resolutionMethod != ResolutionMethod.FIRST_FOUND |
||||
&& this.resolutionMethod != ResolutionMethod.OVERRIDE_AND_IGNORE) { |
||||
throw new IllegalStateException(ex); |
||||
} |
||||
if (this.logger.isWarnEnabled()) { |
||||
this.logger.warn("Could not load map from " + resource + ": " |
||||
+ ex.getMessage()); |
||||
} |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
private Map<String, Object> asMap(Object object) { |
||||
// YAML can have numbers as keys
|
||||
Map<String, Object> result = new LinkedHashMap<String, Object>(); |
||||
if (!(object instanceof Map)) { |
||||
// A document can be a text literal
|
||||
result.put("document", object); |
||||
return result; |
||||
} |
||||
|
||||
Map<Object, Object> map = (Map<Object, Object>) object; |
||||
for (Entry<Object, Object> entry : map.entrySet()) { |
||||
Object value = entry.getValue(); |
||||
if (value instanceof Map) { |
||||
value = asMap(value); |
||||
} |
||||
Object key = entry.getKey(); |
||||
if (key instanceof CharSequence) { |
||||
result.put(key.toString(), value); |
||||
} |
||||
else { |
||||
// It has to be a map key in this case
|
||||
result.put("[" + key.toString() + "]", value); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
private boolean process(Map<String, Object> map, MatchCallback callback) { |
||||
|
||||
Properties properties = new Properties(); |
||||
assignProperties(properties, map, null); |
||||
|
||||
if (this.documentMatchers.isEmpty()) { |
||||
if (this.logger.isDebugEnabled()) { |
||||
this.logger.debug("Merging document (no matchers set)" + map); |
||||
} |
||||
callback.process(properties, map); |
||||
return true; |
||||
} |
||||
|
||||
MatchStatus result = MatchStatus.ABSTAIN; |
||||
for (DocumentMatcher matcher : this.documentMatchers) { |
||||
MatchStatus match = matcher.matches(properties); |
||||
result = MatchStatus.getMostSpecific(match, result); |
||||
if (match == MatchStatus.FOUND) { |
||||
if (this.logger.isDebugEnabled()) { |
||||
this.logger.debug("Matched document with document matcher: " |
||||
+ properties); |
||||
} |
||||
callback.process(properties, map); |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
if (result == MatchStatus.ABSTAIN && this.matchDefault) { |
||||
if (this.logger.isDebugEnabled()) { |
||||
this.logger.debug("Matched document with default matcher: " + map); |
||||
} |
||||
callback.process(properties, map); |
||||
return true; |
||||
} |
||||
|
||||
this.logger.debug("Unmatched document"); |
||||
return false; |
||||
} |
||||
|
||||
private void assignProperties(Properties properties, Map<String, Object> input, |
||||
String path) { |
||||
for (Entry<String, Object> entry : input.entrySet()) { |
||||
String key = entry.getKey(); |
||||
if (StringUtils.hasText(path)) { |
||||
if (key.startsWith("[")) { |
||||
key = path + key; |
||||
} |
||||
else { |
||||
key = path + "." + key; |
||||
} |
||||
} |
||||
Object value = entry.getValue(); |
||||
if (value instanceof String) { |
||||
properties.put(key, value); |
||||
} |
||||
else if (value instanceof Map) { |
||||
// Need a compound key
|
||||
@SuppressWarnings("unchecked") |
||||
Map<String, Object> map = (Map<String, Object>) value; |
||||
assignProperties(properties, map, key); |
||||
} |
||||
else if (value instanceof Collection) { |
||||
// Need a compound key
|
||||
@SuppressWarnings("unchecked") |
||||
Collection<Object> collection = (Collection<Object>) value; |
||||
int count = 0; |
||||
for (Object object : collection) { |
||||
assignProperties(properties, |
||||
Collections.singletonMap("[" + (count++) + "]", object), key); |
||||
} |
||||
} |
||||
else { |
||||
properties.put(key, value == null ? "" : value); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Callback interface used to process properties in a resulting map. |
||||
*/ |
||||
public interface MatchCallback { |
||||
|
||||
/** |
||||
* Process the properties. |
||||
* @param properties the properties to process |
||||
* @param map a mutable result map |
||||
*/ |
||||
void process(Properties properties, Map<String, Object> map); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Strategy interface used the test if properties match. |
||||
*/ |
||||
public interface DocumentMatcher { |
||||
|
||||
/** |
||||
* Test if the given properties match. |
||||
* @param properties the properties to test |
||||
* @return the status of the match. |
||||
*/ |
||||
MatchStatus matches(Properties properties); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Status returned from {@link DocumentMatcher#matches(Properties)} |
||||
*/ |
||||
public static enum MatchStatus { |
||||
|
||||
/** |
||||
* A match was found. |
||||
*/ |
||||
FOUND, |
||||
|
||||
/** |
||||
* No match was found. |
||||
*/ |
||||
NOT_FOUND, |
||||
|
||||
/** |
||||
* The matcher should not be considered. |
||||
*/ |
||||
ABSTAIN; |
||||
|
||||
/** |
||||
* Compare two {@link MatchStatus} items, returning the most specific status. |
||||
*/ |
||||
public static MatchStatus getMostSpecific(MatchStatus a, MatchStatus b) { |
||||
return a.ordinal() < b.ordinal() ? a : b; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Resolution methods. |
||||
*/ |
||||
public static enum ResolutionMethod { |
||||
|
||||
/** |
||||
* Replace values from earlier in the list. |
||||
*/ |
||||
OVERRIDE, |
||||
|
||||
/** |
||||
* Replace values from earlier in the list, ignoring any failures. |
||||
*/ |
||||
OVERRIDE_AND_IGNORE, |
||||
|
||||
/** |
||||
* Take the first resource in the list that exists and use just that. |
||||
*/ |
||||
FIRST_FOUND |
||||
} |
||||
|
||||
} |
||||
@ -1,109 +0,0 @@
@@ -1,109 +0,0 @@
|
||||
/* |
||||
* Copyright 2012 the original author or 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 org.springframework.boot.yaml; |
||||
|
||||
import java.util.Map; |
||||
import java.util.Properties; |
||||
|
||||
import org.springframework.beans.factory.FactoryBean; |
||||
|
||||
/** |
||||
* Factory for Java Properties that reads from a YAML source. YAML is a nice |
||||
* human-readable format for configuration, and it has some useful hierarchical |
||||
* properties. It's more or less a superset of JSON, so it has a lot of similar features. |
||||
* The Properties created by this factory have nested paths for hierarchical objects, so |
||||
* for instance this YAML |
||||
* |
||||
* <pre class="code"> |
||||
* environments: |
||||
* dev: |
||||
* url: http://dev.bar.com
|
||||
* name: Developer Setup |
||||
* prod: |
||||
* url: http://foo.bar.com
|
||||
* name: My Cool App |
||||
* </pre> |
||||
* |
||||
* is transformed into these Properties: |
||||
* |
||||
* <pre class="code"> |
||||
* environments.dev.url=http://dev.bar.com
|
||||
* environments.dev.name=Developer Setup |
||||
* environments.prod.url=http://foo.bar.com
|
||||
* environments.prod.name=My Cool App |
||||
* </pre> |
||||
* |
||||
* Lists are represented as comma-separated values (useful for simple String values) and |
||||
* also as property keys with <code>[]</code> dereferencers, for example this YAML: |
||||
* |
||||
* <pre class="code"> |
||||
* servers: |
||||
* - dev.bar.com |
||||
* - foo.bar.com |
||||
* </pre> |
||||
* |
||||
* becomes java Properties like this: |
||||
* |
||||
* <pre class="code"> |
||||
* servers=dev.bar.com,foo.bar.com |
||||
* servers[0]=dev.bar.com |
||||
* servers[1]=foo.bar.com |
||||
* </pre> |
||||
* |
||||
* @author Dave Syer |
||||
*/ |
||||
public class YamlPropertiesFactoryBean extends YamlProcessor implements |
||||
FactoryBean<Properties> { |
||||
|
||||
private boolean singleton = true; |
||||
|
||||
private Properties instance; |
||||
|
||||
@Override |
||||
public Properties getObject() { |
||||
if (!this.singleton || this.instance == null) { |
||||
final Properties result = new Properties(); |
||||
process(new MatchCallback() { |
||||
@Override |
||||
public void process(Properties properties, Map<String, Object> map) { |
||||
result.putAll(properties); |
||||
} |
||||
}); |
||||
this.instance = result; |
||||
} |
||||
return this.instance; |
||||
} |
||||
|
||||
@Override |
||||
public Class<?> getObjectType() { |
||||
return Properties.class; |
||||
} |
||||
|
||||
/** |
||||
* Set if a singleton should be created, or a new object on each request otherwise. |
||||
* Default is <code>true</code> (a singleton). |
||||
*/ |
||||
public void setSingleton(boolean singleton) { |
||||
this.singleton = singleton; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isSingleton() { |
||||
return this.singleton; |
||||
} |
||||
|
||||
} |
||||
@ -1,109 +0,0 @@
@@ -1,109 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2013 the original author or 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 org.springframework.boot.yaml; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.boot.yaml.YamlProcessor.ResolutionMethod; |
||||
import org.springframework.core.io.AbstractResource; |
||||
import org.springframework.core.io.ByteArrayResource; |
||||
import org.springframework.core.io.FileSystemResource; |
||||
import org.springframework.core.io.Resource; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertTrue; |
||||
|
||||
/** |
||||
* Tests for {@link YamlMapFactoryBean}. |
||||
* |
||||
* @author Dave Syer |
||||
*/ |
||||
public class YamlMapFactoryBeanTests { |
||||
|
||||
private final YamlMapFactoryBean factory = new YamlMapFactoryBean(); |
||||
|
||||
@Test |
||||
public void testSetIgnoreResourceNotFound() throws Exception { |
||||
this.factory |
||||
.setResolutionMethod(YamlMapFactoryBean.ResolutionMethod.OVERRIDE_AND_IGNORE); |
||||
this.factory.setResources(new FileSystemResource[] { new FileSystemResource( |
||||
"non-exsitent-file.yml") }); |
||||
assertEquals(0, this.factory.getObject().size()); |
||||
} |
||||
|
||||
@Test(expected = IllegalStateException.class) |
||||
public void testSetBarfOnResourceNotFound() throws Exception { |
||||
this.factory.setResources(new FileSystemResource[] { new FileSystemResource( |
||||
"non-exsitent-file.yml") }); |
||||
assertEquals(0, this.factory.getObject().size()); |
||||
} |
||||
|
||||
@Test |
||||
public void testGetObject() throws Exception { |
||||
this.factory.setResources(new ByteArrayResource[] { new ByteArrayResource( |
||||
"foo: bar".getBytes()) }); |
||||
assertEquals(1, this.factory.getObject().size()); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
@Test |
||||
public void testOverrideAndremoveDefaults() throws Exception { |
||||
this.factory.setResources(new ByteArrayResource[] { |
||||
new ByteArrayResource("foo:\n bar: spam".getBytes()), |
||||
new ByteArrayResource("foo:\n spam: bar".getBytes()) }); |
||||
assertEquals(1, this.factory.getObject().size()); |
||||
assertEquals(2, |
||||
((Map<String, Object>) this.factory.getObject().get("foo")).size()); |
||||
} |
||||
|
||||
@Test |
||||
public void testFirstFound() throws Exception { |
||||
this.factory.setResolutionMethod(ResolutionMethod.FIRST_FOUND); |
||||
this.factory.setResources(new Resource[] { new AbstractResource() { |
||||
@Override |
||||
public String getDescription() { |
||||
return "non-existent"; |
||||
} |
||||
|
||||
@Override |
||||
public InputStream getInputStream() throws IOException { |
||||
throw new IOException("planned"); |
||||
} |
||||
}, new ByteArrayResource("foo:\n spam: bar".getBytes()) }); |
||||
assertEquals(1, this.factory.getObject().size()); |
||||
} |
||||
|
||||
@Test |
||||
public void testMapWithPeriodsInKey() throws Exception { |
||||
this.factory.setResources(new ByteArrayResource[] { new ByteArrayResource( |
||||
"foo:\n ? key1.key2\n : value".getBytes()) }); |
||||
Map<String, Object> map = this.factory.getObject(); |
||||
assertEquals(1, map.size()); |
||||
assertTrue(map.containsKey("foo")); |
||||
Object object = map.get("foo"); |
||||
assertTrue(object instanceof LinkedHashMap); |
||||
@SuppressWarnings("unchecked") |
||||
Map<String, Object> sub = (Map<String, Object>) object; |
||||
assertTrue(sub.containsKey("key1.key2")); |
||||
assertEquals("value", sub.get("key1.key2")); |
||||
} |
||||
|
||||
} |
||||
@ -1,138 +0,0 @@
@@ -1,138 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2014 the original author or 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 org.springframework.boot.yaml; |
||||
|
||||
import java.util.Map; |
||||
import java.util.Properties; |
||||
|
||||
import org.junit.Rule; |
||||
import org.junit.Test; |
||||
import org.junit.rules.ExpectedException; |
||||
import org.springframework.boot.yaml.YamlProcessor.MatchCallback; |
||||
import org.springframework.core.io.ByteArrayResource; |
||||
import org.springframework.core.io.Resource; |
||||
import org.yaml.snakeyaml.parser.ParserException; |
||||
import org.yaml.snakeyaml.scanner.ScannerException; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
|
||||
/** |
||||
* Tests for {@link YamlProcessor}. |
||||
* |
||||
* @author Dave Syer |
||||
*/ |
||||
public class YamlProcessorTests { |
||||
|
||||
private final YamlProcessor processor = new YamlProcessor() { |
||||
}; |
||||
|
||||
@Rule |
||||
public ExpectedException exception = ExpectedException.none(); |
||||
|
||||
@Test |
||||
public void arrayConvertedToIndexedBeanReference() { |
||||
this.processor.setResources(new Resource[] { new ByteArrayResource( |
||||
"foo: bar\nbar: [1,2,3]".getBytes()) }); |
||||
this.processor.process(new MatchCallback() { |
||||
@Override |
||||
public void process(Properties properties, Map<String, Object> map) { |
||||
assertEquals(1, properties.get("bar[0]")); |
||||
assertEquals(2, properties.get("bar[1]")); |
||||
assertEquals(3, properties.get("bar[2]")); |
||||
assertEquals(4, properties.size()); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void testStringResource() throws Exception { |
||||
this.processor.setResources(new Resource[] { new ByteArrayResource( |
||||
"foo # a document that is a literal".getBytes()) }); |
||||
this.processor.process(new MatchCallback() { |
||||
@Override |
||||
public void process(Properties properties, Map<String, Object> map) { |
||||
assertEquals("foo", map.get("document")); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void testBadDocumentStart() throws Exception { |
||||
this.processor.setResources(new Resource[] { new ByteArrayResource( |
||||
"foo # a document\nbar: baz".getBytes()) }); |
||||
this.exception.expect(ParserException.class); |
||||
this.exception.expectMessage("line 2, column 1"); |
||||
this.processor.process(new MatchCallback() { |
||||
@Override |
||||
public void process(Properties properties, Map<String, Object> map) { |
||||
} |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void testBadResource() throws Exception { |
||||
this.processor.setResources(new Resource[] { new ByteArrayResource( |
||||
"foo: bar\ncd\nspam:\n foo: baz".getBytes()) }); |
||||
this.exception.expect(ScannerException.class); |
||||
this.exception.expectMessage("line 3, column 1"); |
||||
this.processor.process(new MatchCallback() { |
||||
@Override |
||||
public void process(Properties properties, Map<String, Object> map) { |
||||
} |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void mapConvertedToIndexedBeanReference() { |
||||
this.processor.setResources(new Resource[] { new ByteArrayResource( |
||||
"foo: bar\nbar:\n spam: bucket".getBytes()) }); |
||||
this.processor.process(new MatchCallback() { |
||||
@Override |
||||
public void process(Properties properties, Map<String, Object> map) { |
||||
// System.err.println(properties);
|
||||
assertEquals("bucket", properties.get("bar.spam")); |
||||
assertEquals(2, properties.size()); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void integerKeyBehaves() { |
||||
this.processor.setResources(new Resource[] { new ByteArrayResource( |
||||
"foo: bar\n1: bar".getBytes()) }); |
||||
this.processor.process(new MatchCallback() { |
||||
@Override |
||||
public void process(Properties properties, Map<String, Object> map) { |
||||
assertEquals("bar", properties.get("[1]")); |
||||
assertEquals(2, properties.size()); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void integerDeepKeyBehaves() { |
||||
this.processor.setResources(new Resource[] { new ByteArrayResource( |
||||
"foo:\n 1: bar".getBytes()) }); |
||||
this.processor.process(new MatchCallback() { |
||||
|
||||
@Override |
||||
public void process(Properties properties, Map<String, Object> map) { |
||||
assertEquals("bar", properties.get("foo[1]")); |
||||
assertEquals(1, properties.size()); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
@ -1,246 +0,0 @@
@@ -1,246 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2014 the original author or 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 org.springframework.boot.yaml; |
||||
|
||||
import java.util.Map; |
||||
import java.util.Properties; |
||||
|
||||
import org.junit.Ignore; |
||||
import org.junit.Rule; |
||||
import org.junit.Test; |
||||
import org.junit.rules.ExpectedException; |
||||
import org.springframework.boot.yaml.YamlProcessor.DocumentMatcher; |
||||
import org.springframework.boot.yaml.YamlProcessor.MatchStatus; |
||||
import org.springframework.boot.yaml.YamlProcessor.ResolutionMethod; |
||||
import org.springframework.core.io.ByteArrayResource; |
||||
import org.springframework.core.io.ClassPathResource; |
||||
import org.springframework.core.io.Resource; |
||||
import org.yaml.snakeyaml.Yaml; |
||||
import org.yaml.snakeyaml.scanner.ScannerException; |
||||
|
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.hamcrest.Matchers.nullValue; |
||||
import static org.junit.Assert.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link YamlPropertiesFactoryBean}. |
||||
* |
||||
* @author Dave Syer |
||||
*/ |
||||
public class YamlPropertiesFactoryBeanTests { |
||||
|
||||
@Rule |
||||
public ExpectedException exception = ExpectedException.none(); |
||||
|
||||
@Test |
||||
public void testLoadResource() throws Exception { |
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); |
||||
factory.setResources(new Resource[] { new ByteArrayResource( |
||||
"foo: bar\nspam:\n foo: baz".getBytes()) }); |
||||
Properties properties = factory.getObject(); |
||||
assertThat(properties.getProperty("foo"), equalTo("bar")); |
||||
assertThat(properties.getProperty("spam.foo"), equalTo("baz")); |
||||
} |
||||
|
||||
@Test |
||||
public void testBadResource() throws Exception { |
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); |
||||
factory.setResources(new Resource[] { new ByteArrayResource( |
||||
"foo: bar\ncd\nspam:\n foo: baz".getBytes()) }); |
||||
this.exception.expect(ScannerException.class); |
||||
this.exception.expectMessage("line 3, column 1"); |
||||
factory.getObject(); |
||||
} |
||||
|
||||
@Test |
||||
public void testLoadResourcesWithOverride() throws Exception { |
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); |
||||
factory.setResources(new Resource[] { |
||||
new ByteArrayResource("foo: bar\nspam:\n foo: baz".getBytes()), |
||||
new ByteArrayResource("foo:\n bar: spam".getBytes()) }); |
||||
Properties properties = factory.getObject(); |
||||
assertThat(properties.getProperty("foo"), equalTo("bar")); |
||||
assertThat(properties.getProperty("spam.foo"), equalTo("baz")); |
||||
assertThat(properties.getProperty("foo.bar"), equalTo("spam")); |
||||
} |
||||
|
||||
@Test |
||||
@Ignore("We can't fail on duplicate keys because the Map is created by the YAML library") |
||||
public void testLoadResourcesWithInternalOverride() throws Exception { |
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); |
||||
factory.setResources(new Resource[] { new ByteArrayResource( |
||||
"foo: bar\nspam:\n foo: baz\nfoo: bucket".getBytes()) }); |
||||
Properties properties = factory.getObject(); |
||||
assertThat(properties.getProperty("foo"), equalTo("bar")); |
||||
} |
||||
|
||||
@Test |
||||
@Ignore("We can't fail on duplicate keys because the Map is created by the YAML library") |
||||
public void testLoadResourcesWithNestedInternalOverride() throws Exception { |
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); |
||||
factory.setResources(new Resource[] { new ByteArrayResource( |
||||
"foo:\n bar: spam\n foo: baz\nbreak: it\nfoo: bucket".getBytes()) }); |
||||
Properties properties = factory.getObject(); |
||||
assertThat(properties.getProperty("foo.bar"), equalTo("spam")); |
||||
} |
||||
|
||||
@Test |
||||
public void testLoadResourceWithMultipleDocuments() throws Exception { |
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); |
||||
factory.setResources(new Resource[] { new ByteArrayResource( |
||||
"foo: bar\nspam: baz\n---\nfoo: bag".getBytes()) }); |
||||
Properties properties = factory.getObject(); |
||||
assertThat(properties.getProperty("foo"), equalTo("bag")); |
||||
assertThat(properties.getProperty("spam"), equalTo("baz")); |
||||
} |
||||
|
||||
@Test |
||||
public void testLoadResourceWithSelectedDocuments() throws Exception { |
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); |
||||
factory.setResources(new Resource[] { new ByteArrayResource( |
||||
"foo: bar\nspam: baz\n---\nfoo: bag\nspam: bad".getBytes()) }); |
||||
factory.setDocumentMatchers(new DocumentMatcher() { |
||||
@Override |
||||
public MatchStatus matches(Properties properties) { |
||||
return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND |
||||
: MatchStatus.NOT_FOUND; |
||||
} |
||||
}); |
||||
Properties properties = factory.getObject(); |
||||
assertThat(properties.getProperty("foo"), equalTo("bag")); |
||||
assertThat(properties.getProperty("spam"), equalTo("bad")); |
||||
} |
||||
|
||||
@Test |
||||
public void testLoadResourceWithDefaultMatch() throws Exception { |
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); |
||||
factory.setMatchDefault(true); |
||||
factory.setResources(new Resource[] { new ByteArrayResource( |
||||
"one: two\n---\nfoo: bar\nspam: baz\n---\nfoo: bag\nspam: bad".getBytes()) }); |
||||
factory.setDocumentMatchers(new DocumentMatcher() { |
||||
@Override |
||||
public MatchStatus matches(Properties properties) { |
||||
if (!properties.containsKey("foo")) { |
||||
return MatchStatus.ABSTAIN; |
||||
} |
||||
return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND |
||||
: MatchStatus.NOT_FOUND; |
||||
} |
||||
}); |
||||
Properties properties = factory.getObject(); |
||||
assertThat(properties.getProperty("foo"), equalTo("bag")); |
||||
assertThat(properties.getProperty("spam"), equalTo("bad")); |
||||
assertThat(properties.getProperty("one"), equalTo("two")); |
||||
} |
||||
|
||||
@Test |
||||
public void testLoadResourceWithoutDefaultMatch() throws Exception { |
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); |
||||
factory.setMatchDefault(false); |
||||
factory.setResources(new Resource[] { new ByteArrayResource( |
||||
"one: two\n---\nfoo: bar\nspam: baz\n---\nfoo: bag\nspam: bad".getBytes()) }); |
||||
factory.setDocumentMatchers(new DocumentMatcher() { |
||||
@Override |
||||
public MatchStatus matches(Properties properties) { |
||||
if (!properties.containsKey("foo")) { |
||||
return MatchStatus.ABSTAIN; |
||||
} |
||||
return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND |
||||
: MatchStatus.NOT_FOUND; |
||||
} |
||||
}); |
||||
Properties properties = factory.getObject(); |
||||
assertThat(properties.getProperty("foo"), equalTo("bag")); |
||||
assertThat(properties.getProperty("spam"), equalTo("bad")); |
||||
assertThat(properties.getProperty("one"), nullValue()); |
||||
} |
||||
|
||||
@Test |
||||
public void testLoadResourceWithDefaultMatchSkippingMissedMatch() throws Exception { |
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); |
||||
factory.setMatchDefault(true); |
||||
factory.setResources(new Resource[] { new ByteArrayResource( |
||||
"one: two\n---\nfoo: bag\nspam: bad\n---\nfoo: bar\nspam: baz".getBytes()) }); |
||||
factory.setDocumentMatchers(new DocumentMatcher() { |
||||
@Override |
||||
public MatchStatus matches(Properties properties) { |
||||
if (!properties.containsKey("foo")) { |
||||
return MatchStatus.ABSTAIN; |
||||
} |
||||
return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND |
||||
: MatchStatus.NOT_FOUND; |
||||
} |
||||
}); |
||||
Properties properties = factory.getObject(); |
||||
assertThat(properties.getProperty("foo"), equalTo("bag")); |
||||
assertThat(properties.getProperty("spam"), equalTo("bad")); |
||||
assertThat(properties.getProperty("one"), equalTo("two")); |
||||
} |
||||
|
||||
@Test |
||||
public void testLoadNonExistentResource() throws Exception { |
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); |
||||
factory.setResolutionMethod(ResolutionMethod.OVERRIDE_AND_IGNORE); |
||||
factory.setResources(new Resource[] { new ClassPathResource("no-such-file.yml") }); |
||||
Properties properties = factory.getObject(); |
||||
assertThat(properties.size(), equalTo(0)); |
||||
} |
||||
|
||||
@Test |
||||
public void testLoadNull() throws Exception { |
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); |
||||
factory.setResources(new Resource[] { new ByteArrayResource("foo: bar\nspam:" |
||||
.getBytes()) }); |
||||
Properties properties = factory.getObject(); |
||||
assertThat(properties.getProperty("foo"), equalTo("bar")); |
||||
assertThat(properties.getProperty("spam"), equalTo("")); |
||||
} |
||||
|
||||
@Test |
||||
public void testLoadArrayOfString() throws Exception { |
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); |
||||
factory.setResources(new Resource[] { new ByteArrayResource("foo:\n- bar\n- baz" |
||||
.getBytes()) }); |
||||
Properties properties = factory.getObject(); |
||||
assertThat(properties.getProperty("foo[0]"), equalTo("bar")); |
||||
assertThat(properties.getProperty("foo[1]"), equalTo("baz")); |
||||
} |
||||
|
||||
@Test |
||||
public void testLoadArrayOfObject() throws Exception { |
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); |
||||
factory.setResources(new Resource[] { new ByteArrayResource( |
||||
"foo:\n- bar:\n spam: crap\n- baz\n- one: two\n three: four" |
||||
.getBytes()) }); |
||||
Properties properties = factory.getObject(); |
||||
assertThat(properties.getProperty("foo[0].bar.spam"), equalTo("crap")); |
||||
assertThat(properties.getProperty("foo[1]"), equalTo("baz")); |
||||
assertThat(properties.getProperty("foo[2].one"), equalTo("two")); |
||||
assertThat(properties.getProperty("foo[2].three"), equalTo("four")); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
@Test |
||||
public void testYaml() { |
||||
Yaml yaml = new Yaml(); |
||||
Map<String, ?> map = yaml.loadAs("foo: bar\nspam:\n foo: baz", Map.class); |
||||
assertThat(map.get("foo"), equalTo((Object) "bar")); |
||||
assertThat(((Map<String, Object>) map.get("spam")).get("foo"), |
||||
equalTo((Object) "baz")); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue