/*
* Copyright (c) 2012. Piraso Alvin R. de Leon. All Rights Reserved.
*
* See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The Piraso 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.piraso.server.service;
import org.piraso.api.GeneralPreferenceEnum;
import org.piraso.api.JacksonUtils;
import org.piraso.api.Preferences;
import org.piraso.api.entry.Entry;
import org.piraso.api.entry.MessageEntry;
import org.piraso.api.io.EntryReadAdapter;
import org.piraso.api.io.EntryReadEvent;
import org.piraso.api.io.PirasoEntryReader;
import org.piraso.server.TestPirasoRequest;
import org.piraso.server.TestPirasoResponse;
import org.codehaus.jackson.map.ObjectMapper;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import static junit.framework.Assert.*;
import static org.mockito.Mockito.*;
/**
* Test for {@link ResponseLoggerServiceImpl} class
*/
public class ResponseLoggerServiceImplTest {
private static final String EXPECTED_MONITORED_ADDRESS = "127.0.0.1";
private ResponseLoggerServiceImpl service;
private MockHttpServletResponse response;
private Preferences preferences;
private User user;
private MockHttpServletRequest request;
private TestPirasoRequest pirasoRequest;
private TestPirasoResponse pirasoResponse;
@Before
public void setUp() throws Exception {
ObjectMapper mapper = JacksonUtils.createMapper();
request = new MockHttpServletRequest();
pirasoRequest = new TestPirasoRequest(request);
response = spy(new MockHttpServletResponse());
pirasoResponse = new TestPirasoResponse(response);
user = new User(pirasoRequest);
preferences = new Preferences();
preferences.addProperty(GeneralPreferenceEnum.STACK_TRACE_ENABLED.getPropertyName(), true);
request.addParameter("watchedAddr", EXPECTED_MONITORED_ADDRESS);
request.addParameter("preferences", mapper.writeValueAsString(preferences));
service = new ResponseLoggerServiceImpl(user, pirasoRequest, pirasoResponse);
}
@Test
public void testMonitoredAddress() throws Exception {
request.removeParameter("watchedAddr");
request.setRemoteAddr("remoteAddr");
service = new ResponseLoggerServiceImpl(user, pirasoRequest, pirasoResponse);
assertEquals("remoteAddr", service.getWatchedAddr());
request.addParameter("watchedAddr", EXPECTED_MONITORED_ADDRESS);
service = new ResponseLoggerServiceImpl(user, pirasoRequest, pirasoResponse);
assertEquals(EXPECTED_MONITORED_ADDRESS, service.getWatchedAddr());
}
@Test
public void testGetters() throws Exception {
assertSame(user, service.getUser());
assertEquals(EXPECTED_MONITORED_ADDRESS, service.getWatchedAddr());
assertEquals(preferences, service.getPreferences());
assertEquals(user.getActivityUuid(), service.getId());
assertTrue(service.isAlive());
}
@Test(expected = ForcedStoppedException.class)
public void testIdleTimeout() throws Exception {
service.setMaxIdleTimeout(100l);
service.start();
}
@Test(expected = ForcedStoppedException.class)
public void testMaxTransferSize() throws Exception {
service.setMaxQueueForceKillSize(2);
service.log(new MessageEntry(1l, "test"));
service.log(new MessageEntry(1l, "test2"));
service.start();
}
@Test
public void testWaitAndStop() throws Exception {
final AtomicBoolean fail = new AtomicBoolean(false);
ExecutorService executor = Executors.newFixedThreadPool(2);
Runnable startServiceRunnable = new Runnable() {
public void run() {
try {
service.start();
} catch (Exception e) {
fail.set(true);
e.printStackTrace();
}
}
};
Runnable logMessagesRunnable = new Runnable() {
public void run() {
try {
service.stopAndWait(3000l);
} catch (Exception e) {
fail.set(true);
e.printStackTrace();
}
}
};
Future future = executor.submit(startServiceRunnable);
executor.submit(logMessagesRunnable);
future.get();
executor.shutdown();
if(fail.get()) {
fail("failure see exception trace.");
}
// no harm invoking it again
service.stopAndWait(1000l);
assertFalse(service.isAlive());
}
@Test
public void testLogging() throws IOException, TransformerConfigurationException, ParserConfigurationException, ExecutionException, InterruptedException, SAXException {
final AtomicBoolean fail = new AtomicBoolean(false);
ExecutorService executor = Executors.newFixedThreadPool(2);
final List<MessageEntry> expectedEntries = new ArrayList<MessageEntry>() {{
for(int i = 0; i < 1000; i++) {
add(new MessageEntry(1l, "test_" + (i + 1)));
}
}};
// stop the service when number of entries is reached.
stopOnWriteTimes(expectedEntries.size());
Runnable startServiceRunnable = new Runnable() {
public void run() {
try {
service.start();
} catch (Exception e) {
fail.set(true);
e.printStackTrace();
}
}
};
Runnable logMessagesRunnable = new Runnable() {
public void run() {
try {
// this entry should be ignored since this will throw an exception
service.log(new ExceptionThrowEntry(1l));
// these entries should succeed
for(MessageEntry entry : expectedEntries) {
service.log(entry);
}
} catch (IOException e) {
fail.set(true);
e.printStackTrace();
}
}
};
Future future = executor.submit(startServiceRunnable);
executor.submit(logMessagesRunnable);
future.get();
executor.shutdown();
if(fail.get()) {
fail("failure see exception trace.");
}
final List<Entry> entriesRead = new ArrayList<Entry>();
PirasoEntryReader reader = new PirasoEntryReader(new ByteArrayInputStream(response.getContentAsByteArray()));
reader.addListener(new EntryReadAdapter() {
@Override
public void readEntry(EntryReadEvent evt) {
entriesRead.add(evt.getEntry());
}
});
// start reading
reader.start();
assertEquals(service.getId(), reader.getId());
assertEquals(expectedEntries.size(), entriesRead.size());
}
/**
* Helper method to ensure that the service stops when number of logs is reached.
*
* @param count the log count before stop
* @throws UnsupportedEncodingException on error
*/
private void stopOnWriteTimes(final int count) throws UnsupportedEncodingException {
PrintWriter writer = spy(response.getWriter());
doReturn(writer).when(response).getWriter();
final AtomicInteger ctr = new AtomicInteger(0);
doAnswer(new Answer() {
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
if(ctr.addAndGet(1) > count) {
service.stop();
}
return invocationOnMock.callRealMethod();
}
}).when(writer).println(anyString());
}
private class ExceptionThrowEntry extends Entry {
private ExceptionThrowEntry(Long requestId) {
setRequestId(requestId);
}
public String getPropertyThatThrowException() {
throw new IllegalStateException("always thrown");
}
}
}