From c9d7b556954167ebb58c6552d3fe29a492b478b4 Mon Sep 17 00:00:00 2001 From: Alberto Codutti Date: Thu, 20 Jun 2024 18:18:52 +0200 Subject: [PATCH] :bug: [Device Management] Fixed DeviceComponentConfiguration propery handling with empty or null values Signed-off-by: Alberto Codutti --- .../metatype/PasswordPropertyAdapter.java | 47 +++++++++++- service/api/pom.xml | 5 +- .../xml/adapters/BooleanPropertyAdapter.java | 5 ++ .../xml/adapters/BytePropertyAdapter.java | 5 ++ .../xml/adapters/CharPropertyAdapter.java | 5 ++ .../ClassBasedXmlPropertyAdapterBase.java | 61 +++++++++++++++- .../xml/adapters/DoublePropertyAdapter.java | 5 ++ .../xml/adapters/FloatPropertyAdapter.java | 5 ++ .../xml/adapters/IntegerPropertyAdapter.java | 5 ++ .../xml/adapters/LongPropertyAdapter.java | 5 ++ .../xml/adapters/ShortPropertyAdapter.java | 5 ++ .../xml/adapters/StringPropertyAdapter.java | 11 +++ .../xml/adapters/XmlPropertiesAdapter.java | 73 ++++++++++++++----- ...ServiceXmlConfigPropertiesAdapterTest.java | 37 ++++++---- .../xml/KuraPasswordPropertyAdapter.java | 47 +++++++++++- 15 files changed, 278 insertions(+), 43 deletions(-) diff --git a/commons/src/main/java/org/eclipse/kapua/commons/configuration/metatype/PasswordPropertyAdapter.java b/commons/src/main/java/org/eclipse/kapua/commons/configuration/metatype/PasswordPropertyAdapter.java index b25a8a689e1..dfbc1bae967 100644 --- a/commons/src/main/java/org/eclipse/kapua/commons/configuration/metatype/PasswordPropertyAdapter.java +++ b/commons/src/main/java/org/eclipse/kapua/commons/configuration/metatype/PasswordPropertyAdapter.java @@ -12,6 +12,7 @@ *******************************************************************************/ package org.eclipse.kapua.commons.configuration.metatype; +import com.google.common.base.Strings; import org.eclipse.kapua.commons.crypto.CryptoUtil; import org.eclipse.kapua.model.xml.XmlPropertyAdapted; import org.eclipse.kapua.model.xml.adapters.ClassBasedXmlPropertyAdapterBase; @@ -28,7 +29,7 @@ public PasswordPropertyAdapter(CryptoUtil cryptoUtil) { } @Override - public boolean canMarshall(Class objectClass) { + public boolean canMarshall(Class objectClass) { return Password.class.equals(objectClass); } @@ -42,19 +43,59 @@ public String marshallValue(Object value) { return cryptoUtil.encodeBase64(value.toString()); } + @Override + public boolean canUnmarshallEmptyString() { + return true; + } + @Override public Password unmarshallValue(String value) { return new Password(cryptoUtil.decodeBase64(value)); } + /** + * Unmarshalls the given value according to {@link XmlPropertyAdapted#isEncrypted()}. + * + * @param value The value to unmarshall. + * @param isEncrypted The {@link XmlPropertyAdapted#isEncrypted()}. + * @return The unmarshalled {@link Password} + * @since 2.1.0 + */ + public Password unmarshallValue(String value, boolean isEncrypted) { + return isEncrypted ? unmarshallValue(value) : new Password(value); + } + @Override public Object unmarshallValues(XmlPropertyAdapted property) { if (!property.getArray()) { - return property.isEncrypted() ? unmarshallValue(property.getValues()[0]) : new Password(property.getValues()[0]); + String[] values = property.getValues(); + + // Values might not have been defined + // ie: + // + // + // + if (values == null || values.length == 0) { + return null; + } + + String value = property.getValues()[0]; + return unmarshallValue(value, property.isEncrypted() && !Strings.isNullOrEmpty(value)); } else { + String[] values = property.getValues(); + + // Values might not have been defined + // ie: + // + // + // + if (values == null) { + return null; + } + return Arrays .stream(property.getValues()) - .map(value -> property.isEncrypted() ? unmarshallValue(value) : new Password(value)) + .map(value -> unmarshallValue(value, property.isEncrypted() && !Strings.isNullOrEmpty(value))) .collect(Collectors.toList()).toArray(); } } diff --git a/service/api/pom.xml b/service/api/pom.xml index e572967314b..6ed8a02f79f 100644 --- a/service/api/pom.xml +++ b/service/api/pom.xml @@ -34,7 +34,10 @@ jaxb-impl runtime - + + com.google.guava + guava + com.fasterxml.jackson.core jackson-databind diff --git a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/BooleanPropertyAdapter.java b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/BooleanPropertyAdapter.java index b56aea3c6fd..074c0a31482 100644 --- a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/BooleanPropertyAdapter.java +++ b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/BooleanPropertyAdapter.java @@ -18,6 +18,11 @@ public BooleanPropertyAdapter() { super(Boolean.class); } + @Override + public boolean canUnmarshallEmptyString() { + return false; + } + @Override public Boolean unmarshallValue(String value) { return Boolean.parseBoolean(value); diff --git a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/BytePropertyAdapter.java b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/BytePropertyAdapter.java index ed3fcb844ce..b9853f46931 100644 --- a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/BytePropertyAdapter.java +++ b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/BytePropertyAdapter.java @@ -18,6 +18,11 @@ public BytePropertyAdapter() { super(Byte.class); } + @Override + public boolean canUnmarshallEmptyString() { + return false; + } + @Override public Byte unmarshallValue(String value) { return Byte.parseByte(value); diff --git a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/CharPropertyAdapter.java b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/CharPropertyAdapter.java index d2a2dd6c3a1..89a9bdd9ceb 100644 --- a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/CharPropertyAdapter.java +++ b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/CharPropertyAdapter.java @@ -18,6 +18,11 @@ public CharPropertyAdapter() { super(Character.class); } + @Override + public boolean canUnmarshallEmptyString() { + return false; + } + @Override public Character unmarshallValue(String value) { return value.charAt(0); diff --git a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/ClassBasedXmlPropertyAdapterBase.java b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/ClassBasedXmlPropertyAdapterBase.java index fefb7616793..ee78b50bdb5 100644 --- a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/ClassBasedXmlPropertyAdapterBase.java +++ b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/ClassBasedXmlPropertyAdapterBase.java @@ -12,6 +12,7 @@ *******************************************************************************/ package org.eclipse.kapua.model.xml.adapters; +import com.google.common.base.Strings; import org.eclipse.kapua.model.xml.XmlPropertyAdapted; import java.lang.reflect.Array; @@ -52,6 +53,9 @@ public void marshallValues(XmlPropertyAdapted property, Object value) { if (nativeValues[i] != null) { stringValues[i] = this.marshallValue(nativeValues[i]); } + else { + stringValues[i] = null; + } } property.setValues(stringValues); } @@ -61,14 +65,69 @@ public String marshallValue(Object object) { return object.toString(); } + /** + * Whether the {@link ClassBasedXmlPropertyAdapterBase} implementation can unmarshall a empty {@link String}. + + * @return {@code true} if it can, {@code false} otherwise. + * @since 2.1.0 + */ + public abstract boolean canUnmarshallEmptyString(); + public abstract T unmarshallValue(String value); @Override public Object unmarshallValues(XmlPropertyAdapted property) { if (!property.getArray()) { + String[] values = property.getValues(); + + // Values might not have been defined + // ie: + // + // + // + if (values == null || values.length == 0) { + return null; + } + + // Value might be empty and some XmlPropertyAdapter can't handle empty values (ie: DoublePropertyAdapter). + // + // ie: + // + // + // + // + // + // + // + // + String value = values[0]; + if (Strings.isNullOrEmpty(value) && !canUnmarshallEmptyString()) { + // FIXME: we should return an empty String, but ESF is not currently handling the DeviceConfiguration recevied. + // This might be the default behaviour to treat single values and arrays values the same... + return null; + } + return unmarshallValue(property.getValues()[0]); } else { - final List items = Arrays.stream(property.getValues()).map(this::unmarshallValue).collect(Collectors.toList()); + String[] values = property.getValues(); + + // Values might not have been defined + // ie: + // + // + // + if (values == null) { + return null; + } + + final List items = Arrays + .stream(property.getValues()) + .map(value -> + !Strings.isNullOrEmpty(value) || canUnmarshallEmptyString() ? + unmarshallValue(value) : + null + ) + .collect(Collectors.toList()); return items.toArray((T[]) Array.newInstance(clazz, items.size())); } } diff --git a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/DoublePropertyAdapter.java b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/DoublePropertyAdapter.java index 726e03d148c..abb30c39a03 100644 --- a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/DoublePropertyAdapter.java +++ b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/DoublePropertyAdapter.java @@ -18,6 +18,11 @@ public DoublePropertyAdapter() { super(Double.class); } + @Override + public boolean canUnmarshallEmptyString() { + return false; + } + @Override public Double unmarshallValue(String value) { return Double.parseDouble(value); diff --git a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/FloatPropertyAdapter.java b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/FloatPropertyAdapter.java index f3049196565..9f0ff400769 100644 --- a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/FloatPropertyAdapter.java +++ b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/FloatPropertyAdapter.java @@ -18,6 +18,11 @@ public FloatPropertyAdapter() { super(Float.class); } + @Override + public boolean canUnmarshallEmptyString() { + return false; + } + @Override public Float unmarshallValue(String value) { return Float.parseFloat(value); diff --git a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/IntegerPropertyAdapter.java b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/IntegerPropertyAdapter.java index 36b293d5ee5..dfab4faffa1 100644 --- a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/IntegerPropertyAdapter.java +++ b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/IntegerPropertyAdapter.java @@ -18,6 +18,11 @@ public IntegerPropertyAdapter() { super(Integer.class); } + @Override + public boolean canUnmarshallEmptyString() { + return false; + } + @Override public Integer unmarshallValue(String property) { return Integer.parseInt(property); diff --git a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/LongPropertyAdapter.java b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/LongPropertyAdapter.java index db3a6b920f6..46a1a7c28cb 100644 --- a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/LongPropertyAdapter.java +++ b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/LongPropertyAdapter.java @@ -18,6 +18,11 @@ public LongPropertyAdapter() { super(Long.class); } + @Override + public boolean canUnmarshallEmptyString() { + return false; + } + @Override public Long unmarshallValue(String property) { return Long.parseLong(property); diff --git a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/ShortPropertyAdapter.java b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/ShortPropertyAdapter.java index ba821904546..9c35360d2b9 100644 --- a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/ShortPropertyAdapter.java +++ b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/ShortPropertyAdapter.java @@ -18,6 +18,11 @@ public ShortPropertyAdapter() { super(Short.class); } + @Override + public boolean canUnmarshallEmptyString() { + return false; + } + @Override public Short unmarshallValue(String property) { return Short.parseShort(property); diff --git a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/StringPropertyAdapter.java b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/StringPropertyAdapter.java index 3bea622b3de..ab116068b0c 100644 --- a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/StringPropertyAdapter.java +++ b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/StringPropertyAdapter.java @@ -18,6 +18,17 @@ public StringPropertyAdapter() { super(String.class); } + /** + * Yes, definitely String can be empty. + * + * @return {@code true} + * @since 2.1.0 + */ + @Override + public boolean canUnmarshallEmptyString() { + return true; + } + @Override public String unmarshallValue(String property) { return property; diff --git a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/XmlPropertiesAdapter.java b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/XmlPropertiesAdapter.java index d4abde52625..bfbc8a2f4c9 100644 --- a/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/XmlPropertiesAdapter.java +++ b/service/api/src/main/java/org/eclipse/kapua/model/xml/adapters/XmlPropertiesAdapter.java @@ -18,6 +18,7 @@ import java.lang.reflect.Array; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -37,18 +38,45 @@ public XmlPropertiesAdapter(Class propertyClass, Supplier adaptedPropertyF @Override public Map unmarshal(V[] properties) { - return Optional.ofNullable(properties) + Map unmarshalledProperties = new HashMap<>(); + + Optional.ofNullable(properties) .map(Arrays::asList) .orElse(Collections.emptyList()) .stream() - .filter(adaptedProp -> adaptedProp.getType() != null) - .filter(adaptedProp -> xmlPropertyAdapters.containsKey((adaptedProp.getType()))) - .collect(Collectors.toMap( - adaptedProp -> adaptedProp.getName(), - adaptedProp -> { - final XmlPropertyAdapter xmlPropertyAdapter = xmlPropertyAdapters.get(adaptedProp.getType()); - return xmlPropertyAdapter.unmarshallValues(adaptedProp); - })); + .filter(adaptedProp -> adaptedProp.getType() == null || xmlPropertyAdapters.containsKey((adaptedProp.getType()))) + .forEach(adaptedProp -> unmarshalledProperties.put(adaptedProp.getName(), unmarshalProperty(adaptedProp))); + + // This was a better way to do it, but Collectors.toMap does not accept `null` values. + // See Collectors#153 for reference. + // .collect(Collectors.toMap( + // adaptedProp -> adaptedProp.getName(), + // adaptedProp -> { + // final XmlPropertyAdapter xmlPropertyAdapter = xmlPropertyAdapters.get(adaptedProp.getType()); + // return xmlPropertyAdapter.unmarshallValues(adaptedProp); + // })); + + return unmarshalledProperties; + } + + /** + * It unmarshal the given {@link XmlPropertyAdapted}. + *

+ * If {@link XmlPropertyAdapted#getType()} is {@code null} it means that {@link XmlPropertyAdapted#getValues()} was {@code null}, so we can't determine its {@link XmlPropertyAdapted#getType()}. + * By the way, if original value was {@code null} we can safely return {@code null}. + * + * @param adaptedProp The {@link XmlPropertyAdapted} to unmarshal + * @return The unmarshalled {@link XmlPropertyAdapted} + * @since 2.1.0 + */ + public Object unmarshalProperty(V adaptedProp) { + if (adaptedProp.getType() != null) { + XmlPropertyAdapter xmlPropertyAdapter = xmlPropertyAdapters.get(adaptedProp.getType()); + return xmlPropertyAdapter.unmarshallValues(adaptedProp); + } + else { + return null; + } } @Override @@ -57,23 +85,28 @@ public V[] marshal(Map props) { .orElse(Collections.emptyMap()) .entrySet() .stream() - .filter(nameAndValue -> nameAndValue.getValue() != null) .map(nameAndValue -> { - final Object value = nameAndValue.getValue(); final V resEntry = adaptedPropertyFactory.get(); resEntry.setName(nameAndValue.getKey()); - xmlPropertyAdapters - .entrySet() - .stream() - .filter(pa -> pa.getValue().canMarshall(value.getClass())) - .findFirst() - .ifPresent(typeToAdapter -> { - resEntry.setType(typeToAdapter.getKey()); - typeToAdapter.getValue().marshallValues(resEntry, value); - }); + + // Some properties can be sent as `null` so we cannot determine the type by the value. + if (nameAndValue.getValue() != null) { + final Object value = nameAndValue.getValue(); + xmlPropertyAdapters + .entrySet() + .stream() + .filter(pa -> pa.getValue().canMarshall(value.getClass())) + .findFirst() + .ifPresent(typeToAdapter -> { + resEntry.setType(typeToAdapter.getKey()); + typeToAdapter.getValue().marshallValues(resEntry, value); + }); + } + return resEntry; }) .collect(Collectors.toList()); + return adaptedProperties.toArray((V[]) Array.newInstance(propertyClass, adaptedProperties.size())); } } diff --git a/service/api/src/test/java/org/eclipse/kapua/service/config/ServiceXmlConfigPropertiesAdapterTest.java b/service/api/src/test/java/org/eclipse/kapua/service/config/ServiceXmlConfigPropertiesAdapterTest.java index 5e52e31f768..21b48305b6b 100644 --- a/service/api/src/test/java/org/eclipse/kapua/service/config/ServiceXmlConfigPropertiesAdapterTest.java +++ b/service/api/src/test/java/org/eclipse/kapua/service/config/ServiceXmlConfigPropertiesAdapterTest.java @@ -90,6 +90,7 @@ public void unmarshalTest() throws Exception { ServiceXmlConfigPropertiesAdapter instance = new ServiceXmlConfigPropertiesAdapter(); + Map expectedProperty0 = new HashMap<>(); Map expectedProperty1 = new HashMap<>(); Map expectedProperty2 = new HashMap<>(); Map expectedProperty3 = new HashMap<>(); @@ -98,33 +99,37 @@ public void unmarshalTest() throws Exception { Map expectedProperty6 = new HashMap<>(); Map expectedProperty7 = new HashMap<>(); Map expectedProperty8 = new HashMap<>(); - Map expectedProperty9 = new HashMap<>(); - Map expectedProperty10 = new HashMap<>(); - expectedProperty2.put("name1", new String[]{"47", "10"}); - expectedProperty3.put("name2", new Long[]{47l, 10l}); - expectedProperty4.put("name3", new Double[]{47d, 10d}); - expectedProperty5.put("name4", new Float[]{47f, 10f}); - expectedProperty6.put("name5", new Integer[]{47, 10}); - expectedProperty7.put("name6", new Byte[]{(byte) 47, (byte) 10}); - expectedProperty8.put("name7", new Character[]{'4', '1'}); - expectedProperty9.put("name8", new Boolean[]{false, false}); - expectedProperty10.put("name9", new Short[]{(short) 47, (short) 10}); - expectedProperties = new Map[]{expectedProperty1, expectedProperty2, expectedProperty3, expectedProperty4, expectedProperty5, expectedProperty6, expectedProperty7, expectedProperty8, expectedProperty9, expectedProperty10}; + expectedProperty0.put("name0", new String[]{"47", "10"}); + expectedProperty1.put("name1", new Long[]{47l, 10l}); + expectedProperty2.put("name2", new Double[]{47d, 10d}); + expectedProperty3.put("name3", new Float[]{47f, 10f}); + expectedProperty4.put("name4", new Integer[]{47, 10}); + expectedProperty5.put("name5", new Byte[]{(byte) 47, (byte) 10}); + expectedProperty6.put("name6", new Character[]{'4', '1'}); + expectedProperty7.put("name7", new Boolean[]{false, false}); + expectedProperty8.put("name8", new Short[]{(short) 47, (short) 10}); + expectedProperties = new Map[]{expectedProperty0, expectedProperty1, expectedProperty2, expectedProperty3, expectedProperty4, expectedProperty5, expectedProperty6, expectedProperty7, expectedProperty8}; String name = "name"; String stringValue1 = "47"; String stringValue2 = "10"; String[] stringValue = {stringValue1, stringValue2}; - ConfigPropertyType[] configPropertyType = {null, ConfigPropertyType.stringType, ConfigPropertyType.longType, - ConfigPropertyType.doubleType, ConfigPropertyType.floatType, ConfigPropertyType.integerType, - ConfigPropertyType.byteType, ConfigPropertyType.charType, ConfigPropertyType.booleanType, + ConfigPropertyType[] configPropertyType = { + ConfigPropertyType.stringType, + ConfigPropertyType.longType, + ConfigPropertyType.doubleType, + ConfigPropertyType.floatType, + ConfigPropertyType.integerType, + ConfigPropertyType.byteType, + ConfigPropertyType.charType, + ConfigPropertyType.booleanType, ConfigPropertyType.shortType}; ServiceXmlConfigPropertiesAdapted serviceXmlConfigPropertiesAdapted = new ServiceXmlConfigPropertiesAdapted(); for (int i = 0; i < configPropertyType.length; i++) { ServiceXmlConfigPropertyAdapted serviceXmlConfigPropertyAdapted1 = new ServiceXmlConfigPropertyAdapted(); ServiceXmlConfigPropertyAdapted serviceXmlConfigPropertyAdapted2 = new ServiceXmlConfigPropertyAdapted(name + i, configPropertyType[i], stringValue); - ServiceXmlConfigPropertyAdapted[] properties1 = new ServiceXmlConfigPropertyAdapted[]{serviceXmlConfigPropertyAdapted1, serviceXmlConfigPropertyAdapted2}; + ServiceXmlConfigPropertyAdapted[] properties1 = new ServiceXmlConfigPropertyAdapted[]{serviceXmlConfigPropertyAdapted2}; Assert.assertThat("Instance of Map expected.", instance.unmarshal(serviceXmlConfigPropertiesAdapted), IsInstanceOf.instanceOf(Map.class)); diff --git a/service/device/call/kura/src/main/java/org/eclipse/kapua/service/device/call/kura/model/configuration/xml/KuraPasswordPropertyAdapter.java b/service/device/call/kura/src/main/java/org/eclipse/kapua/service/device/call/kura/model/configuration/xml/KuraPasswordPropertyAdapter.java index dc4130380ce..c0304bcf36f 100644 --- a/service/device/call/kura/src/main/java/org/eclipse/kapua/service/device/call/kura/model/configuration/xml/KuraPasswordPropertyAdapter.java +++ b/service/device/call/kura/src/main/java/org/eclipse/kapua/service/device/call/kura/model/configuration/xml/KuraPasswordPropertyAdapter.java @@ -12,6 +12,7 @@ *******************************************************************************/ package org.eclipse.kapua.service.device.call.kura.model.configuration.xml; +import com.google.common.base.Strings; import org.eclipse.kapua.commons.crypto.CryptoUtil; import org.eclipse.kapua.model.xml.XmlPropertyAdapted; import org.eclipse.kapua.model.xml.adapters.ClassBasedXmlPropertyAdapterBase; @@ -43,19 +44,61 @@ public String marshallValue(Object value) { return cryptoUtil.encodeBase64(value.toString()); } + @Override + public boolean canUnmarshallEmptyString() { + return true; + } + @Override public KuraPassword unmarshallValue(String value) { return new KuraPassword(cryptoUtil.decodeBase64(value)); } + + /** + * Unmarshalls the given value according to {@link XmlPropertyAdapted#isEncrypted()}. + * + * @param value The value to unmarshall. + * @param isEncrypted The {@link XmlPropertyAdapted#isEncrypted()}. + * @return The unmarshalled {@link KuraPassword} + * @since 2.1.0 + */ + public KuraPassword unmarshallValue(String value, boolean isEncrypted) { + return isEncrypted ? unmarshallValue(value) : new KuraPassword(value); + } + @Override public Object unmarshallValues(XmlPropertyAdapted property) { if (!property.getArray()) { - return property.isEncrypted() ? unmarshallValue(property.getValues()[0]) : new KuraPassword(property.getValues()[0]); + String[] values = property.getValues(); + + // Values might not have been defined + // ie: + // + // + // + if (values == null || values.length == 0) { + return null; + } + + String value = property.getValues()[0]; + return unmarshallValue(value, property.isEncrypted() && !Strings.isNullOrEmpty(value)); + } else { + String[] values = property.getValues(); + + // Values might not have been defined + // ie: + // + // + // + if (values == null) { + return null; + } + return Arrays .stream(property.getValues()) - .map(value -> property.isEncrypted() ? unmarshallValue(value) : new KuraPassword(value)) + .map(value -> unmarshallValue(value, property.isEncrypted() && !Strings.isNullOrEmpty(value))) .collect(Collectors.toList()).toArray(); } }