/**
* Licensed to the Austrian Association for Software Tool Integration (AASTI)
* under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership. The AASTI licenses this file to you 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.openengsb.itests.exam;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.containsString;
import static org.ops4j.pax.exam.OptionUtils.combine;
import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFileExtend;
import java.io.IOException;
import java.util.Hashtable;
import javax.crypto.SecretKey;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TemporaryQueue;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openengsb.core.api.AliveState;
import org.openengsb.core.api.model.ModelWrapper;
import org.openengsb.core.api.model.OpenEngSBModelEntry;
import org.openengsb.core.api.remote.MethodResultMessage;
import org.openengsb.core.api.remote.OutgoingPort;
import org.openengsb.core.common.AbstractOpenEngSBService;
import org.openengsb.core.util.DefaultOsgiUtilsService;
import org.openengsb.core.util.JsonUtils;
import org.openengsb.domain.example.ExampleDomain;
import org.openengsb.domain.example.event.LogEvent;
import org.openengsb.domain.example.model.ExampleRequestModel;
import org.openengsb.domain.example.model.ExampleResponseModel;
import org.openengsb.itests.util.AbstractRemoteTestHelper;
import org.ops4j.pax.exam.Configuration;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.PaxExam;
import org.ops4j.pax.exam.karaf.options.configs.FeaturesCfg;
import org.ops4j.pax.exam.options.extra.VMOption;
import org.osgi.framework.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.SessionCallback;
import org.springframework.jms.support.JmsUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
@RunWith(PaxExam.class)
public class JMSPortIT extends AbstractRemoteTestHelper {
private static final Logger LOGGER = LoggerFactory.getLogger(JMSPortIT.class);
private DefaultOsgiUtilsService utilsService;
private String openwirePort;
@Configuration
public Option[] additionalConfiguration() throws Exception {
return combine(baseConfiguration(),
editConfigurationFileExtend(FeaturesCfg.BOOT, ",openengsb-ports-jms,openengsb-connector-example"),
new VMOption("-Dorg.openengsb.jms.noencrypt=false"),
new VMOption("-Dorg.openengsb.security.noverify=false"));
}
@Override
@Before
public void setUp() throws Exception {
super.setUp();
openwirePort = getOpenwirePort();
additionalJMSSetUp(LOGGER);
utilsService = getOsgiUtils();
}
@Test
public void testJmsPortPresence_shouldBeExportedWithCorrectId() throws Exception {
OutgoingPort serviceWithId = utilsService.getServiceWithId(OutgoingPort.class, "jms-json", 60000);
assertNotNull(serviceWithId);
}
@Test
public void testStartSimpleWorkflow_ShouldReturn42() throws Exception {
EncryptedAnswer answer = sendMessage(METHOD_CALL_STRING);
verifyEncryptedResult(answer.getSessionKey(), answer.getAnswer());
}
@Test
public void testStartSimpleWorkflowWithFilterMethodCall_ShouldReturn42() throws Exception {
EncryptedAnswer answer = sendMessage(METHOD_CALL_STRING_FILTER);
verifyEncryptedResult(answer.getSessionKey(), answer.getAnswer());
}
@Test
public void testSendMethodCallWithWrongAuthentication_shouldFail() throws Exception {
String result = sendMessage(METHOD_CALL_STRING, "admin", "wrong-password").getAnswer();
assertThat(result, containsString("Exception"));
assertThat(result, not(containsString("The answer to life the universe and everything")));
}
@Test
public void testRecordAuditInCoreService_shouldReturnVoid() throws Exception {
EncryptedAnswer answer = sendMessage(VOID_CALL_STRING);
String decryptedResult = decryptResult(answer.getSessionKey(), answer.getAnswer());
assertThat(decryptedResult, containsString("\"type\":\"Void\""));
assertThat(decryptedResult, not(containsString("Exception")));
}
@Test
public void testSendMethodWithModelAsParamter_shouldWork() throws Exception {
registerDummyService();
EncryptedAnswer answer = sendMessage(METHOD_CALL_WITH_MODEL_PARAMETER);
String decryptedResult = decryptResult(answer.getSessionKey(), answer.getAnswer());
ExampleResponseModel model = extractResponseModelFromMethodResult(decryptedResult);
assertThat(decryptedResult.contains("successful"), is(true));
assertThat(model.getResult(), is("successful"));
}
@Test
public void testSendMethodWithModelIncludingTailAsParamter_shouldWork() throws Exception {
registerDummyService();
EncryptedAnswer answer = sendMessage(METHOD_CALL_WITH_MODEL_INCLUDING_TAIL_PARAMETER);
String decryptedResult = decryptResult(answer.getSessionKey(), answer.getAnswer());
ExampleResponseModel model = extractResponseModelFromMethodResult(decryptedResult);
assertThat(decryptedResult.contains("successful with tail"), is(true));
assertThat(model.getResult(), is("successful with tail"));
}
private ExampleResponseModel extractResponseModelFromMethodResult(String jsonMessage) throws Exception {
ObjectMapper mapper = new ObjectMapper();
MethodResultMessage methodResult = mapper.readValue(jsonMessage, MethodResultMessage.class);
JsonUtils.convertResult(methodResult);
return (ExampleResponseModel) methodResult.getResult().getArg();
}
private void registerDummyService() throws Exception {
if (isOsgiServiceAvailable(ExampleDomain.class, "(service.pid=test)")) {
// if we get here, the domain is already registered
return;
}
ExampleDomain service = new DummyService("test");
Hashtable<String, Object> properties = new Hashtable<String, Object>();
properties.put(Constants.SERVICE_PID, "test");
properties.put(Constants.SERVICE_RANKING, -1);
properties.put("location.root", new String[]{ "foo" });
getBundleContext().registerService(ExampleDomain.class.getName(), service, properties);
}
private EncryptedAnswer sendMessage(String methodCall) throws Exception {
return sendMessage(methodCall, "admin", "password");
}
private EncryptedAnswer sendMessage(String methodCall, String username, String password) throws Exception {
JmsTemplate template = prepareActiveMqConnection();
String secureRequest = prepareRequest(methodCall, username, password);
SecretKey sessionKey = generateSessionKey();
String encryptedMessage = encryptMessage(secureRequest, sessionKey);
String answer = sendMessage(template, encryptedMessage);
return new EncryptedAnswer(answer, sessionKey);
}
private String sendMessage(final JmsTemplate template, final String msg) {
String resultString = template.execute(new SessionCallback<String>() {
@Override
public String doInJms(Session session) throws JMSException {
Queue queue = session.createQueue("receive");
MessageProducer producer = session.createProducer(queue);
TemporaryQueue tempQueue = session.createTemporaryQueue();
MessageConsumer consumer = session.createConsumer(tempQueue);
TextMessage message = session.createTextMessage(msg);
message.setJMSReplyTo(tempQueue);
producer.send(message);
TextMessage response = (TextMessage) consumer.receive(35000);
assertThat("server should set the value of the correltion ID to the value of the received message id",
response.getJMSCorrelationID(), is(message.getJMSMessageID()));
JmsUtils.closeMessageProducer(producer);
JmsUtils.closeMessageConsumer(consumer);
return response.getText();
}
}, true);
return resultString;
}
private JmsTemplate prepareActiveMqConnection() throws IOException {
String connection = String.format("failover:(tcp://localhost:%s)?timeout=60000", openwirePort);
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory(connection);
JmsTemplate template = new JmsTemplate(cf);
return template;
}
private class EncryptedAnswer {
private String answer;
private SecretKey sessionKey;
public EncryptedAnswer(String answer, SecretKey sessionKey) {
this.answer = answer;
this.sessionKey = sessionKey;
}
public String getAnswer() {
return answer;
}
public SecretKey getSessionKey() {
return sessionKey;
}
}
public class DummyService extends AbstractOpenEngSBService implements ExampleDomain {
public DummyService(String instanceId) {
super(instanceId);
}
@Override
public String doSomethingWithMessage(String message) {
return "success : " + message;
}
@Override
public String doSomethingWithLogEvent(LogEvent event) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public AliveState getAliveState() {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public ExampleResponseModel doSomethingWithModel(ExampleRequestModel model) {
ExampleResponseModel response = new ExampleResponseModel();
response.setResult("successful");
for (OpenEngSBModelEntry entry : ModelWrapper.wrap(model).getOpenEngSBModelTail()) {
if (entry.getKey().equals("specialKey") && entry.getValue().equals("specialValue")) {
response.setResult("successful with tail");
}
}
return response;
}
}
}