/*
* 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;
import static java.time.Duration.ofMillis;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.mule.runtime.api.message.Message.of;
import static org.mule.runtime.core.api.Event.setCurrentEvent;
import static org.mule.tck.MuleTestUtils.getTestFlow;
import static reactor.core.publisher.Mono.from;
import org.mule.runtime.core.api.Event;
import org.mule.runtime.core.api.construct.Flow;
import org.mule.runtime.core.api.construct.Pipeline;
import org.mule.runtime.core.api.transformer.Transformer;
import org.mule.runtime.core.api.transformer.TransformerException;
import org.mule.runtime.core.transformer.AbstractTransformer;
import org.mule.runtime.core.transformer.simple.ByteArrayToObject;
import org.mule.runtime.core.transformer.simple.SerializableToByteArray;
import org.mule.runtime.core.util.SerializationUtils;
import org.mule.tck.junit4.AbstractMuleContextTestCase;
import java.io.ByteArrayInputStream;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class MuleEventTestCase extends AbstractMuleContextTestCase {
private static String TIMEOUT_ILLEGAL_ARGUMENT_EXCEPTION_MESSAGE = "Timeout on Mono blocking read";
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void testEventSerialization() throws Exception {
setCurrentEvent(testEvent());
Transformer transformer = createSerializableToByteArrayTransformer();
transformer.setMuleContext(muleContext);
Serializable serialized = (Serializable) createSerializableToByteArrayTransformer().transform(testEvent());
assertNotNull(serialized);
ByteArrayToObject trans = new ByteArrayToObject();
trans.setMuleContext(muleContext);
Event deserialized = (Event) trans.transform(serialized);
// Assert that deserialized event is not null
assertNotNull(deserialized);
// Assert that deserialized event has session with same id
assertNotNull(deserialized.getSession());
}
private Transformer createSerializableToByteArrayTransformer() {
Transformer transformer = new SerializableToByteArray();
transformer.setMuleContext(muleContext);
return transformer;
}
@Test
public void testEventSerializationRestart() throws Exception {
// Create and register artifacts
Event event = createEventToSerialize();
// Serialize
Serializable serialized = (Serializable) createSerializableToByteArrayTransformer().transform(event);
assertNotNull(serialized);
// Simulate mule cold restart
muleContext.dispose();
muleContext = createMuleContext();
muleContext.start();
ByteArrayToObject trans = new ByteArrayToObject();
trans.setMuleContext(muleContext);
// Recreate and register artifacts (this would happen if using any kind of static config e.g. XML)
createAndRegisterTransformersEndpointBuilderService();
// Deserialize
Event deserialized = (Event) trans.transform(serialized);
// Assert that deserialized event is not null
assertNotNull(deserialized);
// Assert that deserialized event has session with same id
assertNotNull(deserialized.getSession());
}
private Event createEventToSerialize() throws Exception {
createAndRegisterTransformersEndpointBuilderService();
return testEvent();
}
@Test
public void testMuleEventSerializationWithRawPayload() throws Exception {
StringBuilder payload = new StringBuilder();
// to reproduce issue we must try to serialize something with a payload bigger than 1020 bytes
for (int i = 0; i < 108; i++) {
payload.append("1234567890");
}
Event testEvent = eventBuilder().message(of(new ByteArrayInputStream(payload.toString().getBytes()))).build();
setCurrentEvent(testEvent);
byte[] serializedEvent = muleContext.getObjectSerializer().getExternalProtocol().serialize(testEvent);
testEvent = muleContext.getObjectSerializer().getExternalProtocol().deserialize(serializedEvent);
assertArrayEquals((byte[]) testEvent.getMessage().getPayload().getValue(), payload.toString().getBytes());
}
private void createAndRegisterTransformersEndpointBuilderService() throws Exception {
Transformer trans1 = new TestEventTransformer();
trans1.setName("OptimusPrime");
muleContext.getRegistry().registerTransformer(trans1);
Transformer trans2 = new TestEventTransformer();
trans2.setName("Bumblebee");
muleContext.getRegistry().registerTransformer(trans2);
List<Transformer> transformers = new ArrayList<>();
transformers.add(trans1);
transformers.add(trans2);
}
@Test(expected = UnsupportedOperationException.class)
public void testFlowVarNamesAddImmutable() throws Exception {
Event event = eventBuilder()
.message(of("whatever"))
.addVariable("test", "val")
.build();
event.getVariableNames().add("other");
}
public void testFlowVarNamesRemoveMutable() throws Exception {
Event event = eventBuilder()
.message(of("whatever"))
.addVariable("test", "val")
.build();
event = Event.builder(event).addVariable("test", "val").build();
event.getVariableNames().remove("test");
assertNull(event.getVariable("test").getValue());
}
@Test
public void testFlowVarsNotShared() throws Exception {
Event event = eventBuilder()
.message(of("whatever"))
.addVariable("foo", "bar")
.build();
event = Event.builder(event).addVariable("foo", "bar").build();
Event copy = Event.builder(event).build();
copy = Event.builder(copy).addVariable("foo", "bar2").build();
assertEquals("bar", event.getVariable("foo").getValue());
assertEquals("bar2", copy.getVariable("foo").getValue());
}
@Test(expected = NoSuchElementException.class)
public void testGetFlowVarNonexistent() throws Exception {
testEvent().getVariable("foo").getValue();
}
@Test(expected = NoSuchElementException.class)
public void testGetFlowVarDataTypeNonexistent() throws Exception {
testEvent().getVariable("foo").getDataType();
}
@Test
public void eventContextSerializationNoPipelinePublisherLost() throws Exception {
Event result = testEvent();
Event before = testEvent();
Event after = (Event) SerializationUtils.deserialize(SerializationUtils.serialize(before), muleContext);
after.getContext().success(result);
assertThat(before.getContext().getId(), equalTo(after.getContext().getId()));
// Publisher is not conserved after serialization due to null FlowConstruct so attempting to obtain result via before event
// fails with timeout.
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage(startsWith(TIMEOUT_ILLEGAL_ARGUMENT_EXCEPTION_MESSAGE));
from(before.getContext().getResponsePublisher()).block(ofMillis(BLOCK_TIMEOUT));
}
@Test
public void eventContextSerializationEventContextGarbageCollected() throws Exception {
Flow flow = getTestFlow(muleContext);
Event before = eventBuilder().message(of(null)).flow(flow).build();
String beforeId = before.getContext().getId();
byte[] bytes = SerializationUtils.serialize(before);
before = null;
System.gc();
// The event is never deserialized but it is cleaned up by garbage collection due to WeakReference
assertThat(flow.getSerializationEventContextCache().get(beforeId), is(nullValue()));
}
@Test
public void eventContextSerializationPublisherConserved() throws Exception {
Event result = testEvent();
Event before = eventBuilder().message(of(null)).flow(getTestFlow(muleContext)).build();
Event after = (Event) SerializationUtils.deserialize(SerializationUtils.serialize(before), muleContext);
after.getContext().success(result);
assertThat(before.getContext().getId(), equalTo(after.getContext().getId()));
// Publisher is conserved after serialization so attempting to obtain result via before event is successful.
assertThat(from(before.getContext().getResponsePublisher()).block(), equalTo(result));
// Cache entry is removed on deserialization
assertThat(((Pipeline) before.getFlowConstruct()).getSerializationEventContextCache().get(before.getContext().getId()),
is(nullValue()));
}
private static class TestEventTransformer extends AbstractTransformer {
@Override
public Object doTransform(Object src, Charset encoding) throws TransformerException {
return "Transformed Test Data";
}
}
}