/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.runtime.core.transformer.types; import static java.nio.charset.StandardCharsets.ISO_8859_1; import static java.nio.charset.StandardCharsets.US_ASCII; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.singletonList; import static java.util.Optional.of; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.sameInstance; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; import static org.mule.runtime.api.el.BindingContext.builder; import static org.mule.runtime.api.metadata.DataType.NUMBER; import static org.mule.runtime.api.metadata.DataType.OBJECT; import static org.mule.runtime.api.metadata.DataType.STRING; import static org.mule.runtime.core.util.IOUtils.toByteArray; import org.mule.runtime.api.el.BindingContext; import org.mule.runtime.api.el.ExpressionFunction; import org.mule.runtime.api.message.Message; import org.mule.runtime.api.metadata.DataType; import org.mule.runtime.api.metadata.DataTypeParamsBuilder; import org.mule.runtime.api.metadata.FunctionDataType; import org.mule.runtime.api.metadata.FunctionParameter; import org.mule.runtime.core.internal.metadata.DefaultCollectionDataType; import org.mule.runtime.core.internal.metadata.DefaultFunctionDataType; import org.mule.runtime.core.internal.metadata.DefaultMapDataType; import org.mule.runtime.core.internal.metadata.SimpleDataType; import org.mule.tck.junit4.AbstractMuleTestCase; import org.mule.tck.probe.JUnitLambdaProbe; import org.mule.tck.probe.PollingProber; import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Optional; import java.util.Set; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; public class DataTypeBuilderTestCase extends AbstractMuleTestCase { @Rule public ExpectedException expected = ExpectedException.none(); @Test public void buildSimple() { final DataType dataType = DataType.fromType(String.class); assertThat(dataType, instanceOf(SimpleDataType.class)); assertThat(dataType.getType(), is(equalTo(String.class))); } @Test public void buildCollection() { final DataType dataType = DataType.fromType(Set.class); assertThat(dataType, instanceOf(DefaultCollectionDataType.class)); assertThat(dataType.getType(), is(equalTo(Set.class))); assertThat(((DefaultCollectionDataType) dataType).getItemDataType(), is(OBJECT)); } @Test public void buildFunction() { FunctionDataType dataType = (FunctionDataType) DataType.fromFunction(new SomeFunction()); //Return type assertThat(dataType.getReturnType().isPresent(), is(true)); assertThat(dataType.getReturnType().get(), equalTo(STRING)); //Parameters assertThat(dataType.getParameters(), hasSize(2)); FunctionParameter first = dataType.getParameters().get(0); assertThat(first.getName(), is("fst")); assertThat(first.getType(), equalTo(NUMBER)); assertThat(first.getDefaultValueResolver(), nullValue()); FunctionParameter second = dataType.getParameters().get(1); assertThat(second.getName(), is("snd")); assertThat(second.getType(), equalTo(OBJECT)); //Default assertThat(second.getDefaultValueResolver().getDefaultValue(builder().build()), is("wow")); } @Test public void buildMap() { final DataType dataType = DataType.fromType(HashMap.class); assertThat(dataType, instanceOf(DefaultMapDataType.class)); assertThat(dataType.getType(), is(equalTo(HashMap.class))); assertThat(((DefaultMapDataType) dataType).getKeyDataType(), is(OBJECT)); assertThat(((DefaultMapDataType) dataType).getValueDataType(), is(OBJECT)); } @Test public void buildTypedCollection() { final DataType dataType = DataType.builder().collectionType(List.class).itemType(String.class).build(); assertThat(dataType, instanceOf(DefaultCollectionDataType.class)); assertThat(dataType.getType(), is(equalTo(List.class))); assertThat(((DefaultCollectionDataType) dataType).getItemDataType(), is(STRING)); } @Test public void buildTypedMap() { final DataType dataType = DataType.builder().mapType(HashMap.class).keyType(Number.class).valueType(String.class).build(); assertThat(dataType, instanceOf(DefaultMapDataType.class)); assertThat(dataType.getType(), is(equalTo(HashMap.class))); assertThat(((DefaultMapDataType) dataType).getKeyDataType(), is(NUMBER)); assertThat(((DefaultMapDataType) dataType).getValueDataType(), is(STRING)); } @Test public void buildTypedCollectionFromImplementationClass() { final DataType dataType = DataType.builder().collectionType(SpecificCollection.class).build(); assertThat(dataType, instanceOf(DefaultCollectionDataType.class)); assertThat(dataType.getType(), is(equalTo(SpecificCollection.class))); assertThat(((DefaultCollectionDataType) dataType).getItemDataType(), is(STRING)); } @Test public void buildTypedMapFromImplementationClass() { final DataType dataType = DataType.builder().mapType(SpecificMap.class).build(); assertThat(dataType, instanceOf(DefaultMapDataType.class)); assertThat(dataType.getType(), is(equalTo(SpecificMap.class))); assertThat(((DefaultMapDataType) dataType).getKeyDataType(), is(STRING)); assertThat(((DefaultMapDataType) dataType).getValueDataType(), is(NUMBER)); } @Test public void templateSimple() { final DataType template = DataType.builder().type(String.class).mediaType("text/plain;charset=ASCII").build(); final DataType dataType = DataType.builder(template).build(); assertThat(dataType, instanceOf(SimpleDataType.class)); assertThat(dataType.getType(), is(equalTo(String.class))); assertThat(dataType.getMediaType().getPrimaryType(), is("text")); assertThat(dataType.getMediaType().getSubType(), is("plain")); assertThat(dataType.getMediaType().getCharset().get(), is(US_ASCII)); } @Test public void templateCollection() { final DataType template = DataType.builder().type(Set.class).mediaType("text/plain;charset=ASCII").build(); final DataType dataType = DataType.builder(template).build(); assertThat(dataType, instanceOf(DefaultCollectionDataType.class)); assertThat(dataType.getType(), is(equalTo(Set.class))); assertThat(((DefaultCollectionDataType) dataType).getItemDataType(), is(OBJECT)); assertThat(dataType.getMediaType().getPrimaryType(), is("text")); assertThat(dataType.getMediaType().getSubType(), is("plain")); assertThat(dataType.getMediaType().getCharset().get(), is(US_ASCII)); } @Test public void templateTypedCollection() { final DataType template = DataType.builder() .collectionType(List.class) .itemType(String.class) .itemMediaType("application/json;charset=UTF-8") .mediaType("text/plain;charset=ASCII") .build(); final DataType dataType = DataType.builder(template).build(); assertThat(dataType, instanceOf(DefaultCollectionDataType.class)); assertThat(dataType.getType(), is(equalTo(List.class))); assertThat(((DefaultCollectionDataType) dataType).getItemDataType(), is(STRING)); assertThat(((DefaultCollectionDataType) dataType).getItemDataType().getMediaType().getPrimaryType(), is("application")); assertThat(((DefaultCollectionDataType) dataType).getItemDataType().getMediaType().getSubType(), is("json")); assertThat(((DefaultCollectionDataType) dataType).getItemDataType().getMediaType().getCharset().get(), is(UTF_8)); assertThat(dataType.getMediaType().getPrimaryType(), is("text")); assertThat(dataType.getMediaType().getSubType(), is("plain")); assertThat(dataType.getMediaType().getCharset().get(), is(US_ASCII)); } @Test public void templateMap() { final DataType template = DataType.builder().type(HashMap.class).mediaType("text/plain;charset=ASCII").build(); final DataType dataType = DataType.builder(template).build(); assertThat(dataType, instanceOf(DefaultMapDataType.class)); assertThat(dataType.getType(), is(equalTo(HashMap.class))); assertThat(((DefaultMapDataType) dataType).getKeyDataType(), is(OBJECT)); assertThat(((DefaultMapDataType) dataType).getValueDataType(), is(OBJECT)); assertThat(dataType.getMediaType().getPrimaryType(), is("text")); assertThat(dataType.getMediaType().getSubType(), is("plain")); assertThat(dataType.getMediaType().getCharset().get(), is(US_ASCII)); } @Test public void templateTypedMap() { final DataType template = DataType.builder() .mapType(HashMap.class) .keyType(String.class) .keyMediaType("text/plain;charset=UTF-8") .valueType(Number.class) .valueMediaType("application/json;charset=ISO-8859-1") .mediaType("text/plain;charset=ASCII") .build(); final DataType dataType = DataType.builder(template).build(); assertThat(dataType, instanceOf(DefaultMapDataType.class)); assertThat(dataType.getType(), is(equalTo(HashMap.class))); assertThat(((DefaultMapDataType) dataType).getKeyDataType(), is(STRING)); assertThat(((DefaultMapDataType) dataType).getKeyDataType().getMediaType().getPrimaryType(), is("text")); assertThat(((DefaultMapDataType) dataType).getKeyDataType().getMediaType().getSubType(), is("plain")); assertThat(((DefaultMapDataType) dataType).getKeyDataType().getMediaType().getCharset().get(), is(UTF_8)); assertThat(((DefaultMapDataType) dataType).getValueDataType(), is(NUMBER)); assertThat(((DefaultMapDataType) dataType).getValueDataType().getMediaType().getPrimaryType(), is("application")); assertThat(((DefaultMapDataType) dataType).getValueDataType().getMediaType().getSubType(), is("json")); assertThat(((DefaultMapDataType) dataType).getValueDataType().getMediaType().getCharset().get(), is(ISO_8859_1)); } @Test public void templateFunction() { FunctionParameter functionParameter = new FunctionParameter("fst", NUMBER); final DataType template = DataType.builder() .functionType(SomeFunction.class) .returnType(STRING) .parametersType(singletonList(functionParameter)) .build(); final DataType dataType = DataType.builder(template).build(); assertThat(dataType, instanceOf(DefaultFunctionDataType.class)); assertThat(dataType.getType(), is(equalTo(SomeFunction.class))); assertThat(((DefaultFunctionDataType) dataType).getReturnType().get(), is(STRING)); assertThat(((DefaultFunctionDataType) dataType).getParameters(), hasItems(functionParameter)); } @Test public void proxy() { final Class<?> muleMessageProxy = Proxy.getProxyClass(DataTypeBuilderTestCase.class.getClassLoader(), Message.class); final DataType dataType = DataType.fromType(muleMessageProxy); assertThat(dataType.getType(), is(equalTo(Message.class))); } @Test public void mimeTypeWithEncoding() { final DataType dataType = DataType.builder().mediaType("text/plain;charset=ASCII").build(); assertThat(dataType.getMediaType().getPrimaryType(), is("text")); assertThat(dataType.getMediaType().getSubType(), is("plain")); assertThat(dataType.getMediaType().getCharset().get(), is(US_ASCII)); } @Test public void invalidMimeType() { expected.expect(IllegalArgumentException.class); final DataType dataType = DataType.builder().mediaType("imInvalid").build(); } @Test public void invalidEncoding() { expected.expect(IllegalArgumentException.class); final DataType dataType = DataType.builder().charset("imInvalid").build(); } @Test public void recycleBuilder() { final DataTypeParamsBuilder builder = DataType.builder().type(String.class); builder.build(); expected.expect(IllegalStateException.class); builder.build(); } @Test public void cachedInstances() { final DataTypeParamsBuilder builder1 = DataType.builder().type(String.class); final DataTypeParamsBuilder builder2 = DataType.builder().type(String.class); assertThat(builder1, equalTo(builder2)); assertThat(builder1.build(), sameInstance(builder2.build())); } @Test public void cacheClean() throws InterruptedException, ClassNotFoundException { ClassLoader custom = new ClassLoader(this.getClass().getClassLoader()) { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { if (Message.class.getName().equals(name)) { byte[] classBytes; try { classBytes = toByteArray(this.getClass().getResourceAsStream("/org/mule/runtime/api/message/Message.class")); return this.defineClass(null, classBytes, 0, classBytes.length); } catch (Exception e) { return super.loadClass(name); } } else { return super.loadClass(name); } } }; PhantomReference<ClassLoader> clRef = new PhantomReference<>(custom, new ReferenceQueue<>()); DataType.builder().type(custom.loadClass(Message.class.getName())).build(); custom = null; new PollingProber().check(new JUnitLambdaProbe(() -> { System.gc(); assertThat(clRef.isEnqueued(), is(true)); return true; }, "A hard reference is being mantained to the type of the DataType.")); } private class SpecificMap extends HashMap<String, Number> { } private class SpecificCollection extends LinkedList<String> { } private class SomeFunction implements ExpressionFunction { @Override public Object call(Object[] objects, BindingContext bindingContext) { return null; } @Override public Optional<DataType> returnType() { return of(STRING); } @Override public List<FunctionParameter> parameters() { List<FunctionParameter> parameters = new ArrayList<>(); parameters.add(new FunctionParameter("fst", NUMBER)); parameters.add(new FunctionParameter("snd", OBJECT, ctx -> "wow")); return parameters; } } }