/*
* Copyright 2014-2016 CyberVision, Inc.
*
* Licensed 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.kaaproject.kaa.server.appenders.mongo.appender;
import com.mongodb.DB;
import com.mongodb.DBObject;
import com.mongodb.ServerAddress;
import org.apache.avro.generic.GenericRecord;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.kaaproject.kaa.common.avro.AvroByteArrayConverter;
import org.kaaproject.kaa.common.avro.AvroJsonConverter;
import org.kaaproject.kaa.common.avro.GenericAvroConverter;
import org.kaaproject.kaa.common.dto.EndpointProfileDataDto;
import org.kaaproject.kaa.common.dto.logs.LogAppenderDto;
import org.kaaproject.kaa.common.dto.logs.LogHeaderStructureDto;
import org.kaaproject.kaa.common.dto.logs.LogSchemaDto;
import org.kaaproject.kaa.common.endpoint.gen.BasicEndpointProfile;
import org.kaaproject.kaa.server.appenders.mongo.config.gen.MongoDBCredential;
import org.kaaproject.kaa.server.appenders.mongo.config.gen.MongoDbConfig;
import org.kaaproject.kaa.server.appenders.mongo.config.gen.MongoDbServer;
import org.kaaproject.kaa.server.common.core.algorithms.generation.DefaultRecordGenerationAlgorithm;
import org.kaaproject.kaa.server.common.core.algorithms.generation.DefaultRecordGenerationAlgorithmImpl;
import org.kaaproject.kaa.server.common.core.configuration.RawData;
import org.kaaproject.kaa.server.common.core.configuration.RawDataFactory;
import org.kaaproject.kaa.server.common.core.schema.RawSchema;
import org.kaaproject.kaa.server.common.log.shared.appender.LogAppender;
import org.kaaproject.kaa.server.common.log.shared.appender.LogDeliveryCallback;
import org.kaaproject.kaa.server.common.log.shared.appender.LogEvent;
import org.kaaproject.kaa.server.common.log.shared.appender.LogSchema;
import org.kaaproject.kaa.server.common.log.shared.appender.data.BaseLogEventPack;
import org.kaaproject.kaa.server.common.log.shared.appender.data.BaseProfileInfo;
import org.kaaproject.kaa.server.common.log.shared.appender.data.BaseSchemaInfo;
import org.kaaproject.kaa.server.common.log.shared.appender.data.ProfileInfo;
import org.kaaproject.kaa.server.common.nosql.mongo.dao.MongoDBTestRunner;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.util.ReflectionTestUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Random;
public class MongoDBLogAppenderTest {
private static final Logger LOG = LoggerFactory.getLogger(MongoDBLogAppenderTest.class);
private static final Charset UTF_8 = Charset.forName("UTF-8");
private static final String APPLICATION_ID = "application_id";
private static final String APPLICATION_TOKEN = "application_token";
private static final String TENANT_ID = "tenant_id";
private static final String NEW_APPENDER_NAME = "new name";
private static final String ENDPOINT_KEY = "endpoint key";
private static final String EMPTY_SCHEMA = "{" + "\"type\": \"record\"," + "\"name\": \"Log\","
+ "\"namespace\": \"org.kaaproject.kaa.schema.base\"," + "\"fields\": []" + "}";
private static final String LOG_DATA = "null";
private static final long DATE_CREATED = System.currentTimeMillis();
private static final String SERVER_PROFILE_SCHEMA_FILE = "server_profile_schema.avsc";
private static final String SERVER_PROFILE_CONTENT_FILE = "server_profile_content.json";
// According to the server profile schema file
private static final String SERVER_FIELD_KEY = "country.$";
// According to the server profile content file
private static final String SERVER_FIELD_VALUE = "1.0.$.";
private static final String SERVER_PROFILE = "serverProfile";
private LogAppender logAppender;
@BeforeClass
public static void init() throws Exception {
MongoDBTestRunner.setUp();
}
@AfterClass
public static void afterClass() throws Exception {
MongoDBTestRunner.tearDown();
}
@After
public void after() throws Exception {
LOG.info("Deleting data from MongoDB database");
DB db = MongoDBTestRunner.getDB();
if (db != null) {
db.dropDatabase();
}
}
@Before
public void beforeTest() throws Exception {
// Do not include client and server profiles by default
this.initLogAppender(false, false);
}
@Test
public void changeAppenderNameTest() {
String oldName = logAppender.getName();
logAppender.setName(NEW_APPENDER_NAME);
Assert.assertNotEquals(oldName, logAppender.getName());
Assert.assertEquals(NEW_APPENDER_NAME, logAppender.getName());
}
@Test
public void closeAppenderTest() {
Assert.assertFalse((boolean) ReflectionTestUtils.getField(logAppender, "closed"));
logAppender.close();
Assert.assertTrue((boolean) ReflectionTestUtils.getField(logAppender, "closed"));
}
@Test
public void getConverterTest() {
String schema = EMPTY_SCHEMA;
Assert.assertTrue(((Map) ReflectionTestUtils.getField(logAppender, "converters")).isEmpty());
GenericAvroConverter<GenericRecord> converter1 = ReflectionTestUtils.invokeMethod(logAppender, "getConverter", schema);
Assert.assertEquals(1, ((Map) ReflectionTestUtils.getField(logAppender, "converters")).size());
GenericAvroConverter<GenericRecord> converter2 = ReflectionTestUtils.invokeMethod(logAppender, "getConverter", schema);
Assert.assertEquals(1, ((Map) ReflectionTestUtils.getField(logAppender, "converters")).size());
Assert.assertEquals(converter1, converter2);
}
@Test
// Not throws NullPointerException
public void doAppendClosedTest() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Logger testLogger = Mockito.mock(Logger.class);
Field field = logAppender.getClass().getDeclaredField("LOG");
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, testLogger);
logAppender.close();
TestLogDeliveryCallback callback = new TestLogDeliveryCallback();
EndpointProfileDataDto profileDto = new EndpointProfileDataDto("1", ENDPOINT_KEY, 1, "", 0, null);
BaseLogEventPack logEventPack = new BaseLogEventPack(profileDto, DATE_CREATED, 1, null);
LogSchemaDto schemaDto = new LogSchemaDto();
schemaDto.setVersion(1);
LogSchema schema = new LogSchema(schemaDto, BasicEndpointProfile.SCHEMA$.toString());
logEventPack.setLogSchema(schema);
logAppender.doAppend(logEventPack, callback);
Assert.assertTrue(callback.internallError);
}
@Test
public void doAppendWithCatchIOExceptionTest() throws NoSuchFieldException, SecurityException, IllegalArgumentException,
IllegalAccessException, IOException {
GenericAvroConverter<BasicEndpointProfile> converter = new GenericAvroConverter<BasicEndpointProfile>(BasicEndpointProfile.SCHEMA$);
BasicEndpointProfile theLog = new BasicEndpointProfile("test");
List<LogEvent> events = new ArrayList<>();
LogEvent event1 = new LogEvent();
event1.setLogData(new byte[0]);
LogEvent event2 = new LogEvent();
event2.setLogData(converter.encode(theLog));
LogEvent event3 = new LogEvent();
event3.setLogData(converter.encode(theLog));
events.add(event1);
events.add(event2);
events.add(event3);
LogSchemaDto schemaDto = new LogSchemaDto();
LogSchema schema = new LogSchema(schemaDto, BasicEndpointProfile.SCHEMA$.toString());
EndpointProfileDataDto profileDto = new EndpointProfileDataDto("1", ENDPOINT_KEY, 1, "", 0, null);
BaseLogEventPack logEventPack = new BaseLogEventPack(profileDto, DATE_CREATED, schema.getVersion(), events);
logEventPack.setLogSchema(schema);
LogEventDao logEventDao = Mockito.mock(LogEventDao.class);
LogEventDao eventDao = (LogEventDao) ReflectionTestUtils.getField(logAppender, "logEventDao");
ReflectionTestUtils.setField(logAppender, "logEventDao", logEventDao);
TestLogDeliveryCallback callback = new TestLogDeliveryCallback();
logAppender.doAppend(logEventPack, callback);
Assert.assertTrue(callback.internallError);
Mockito.verify(logEventDao, Mockito.never()).save(Mockito.anyList(), Mockito.<ProfileInfo>anyObject(), Mockito.<ProfileInfo>anyObject(), Mockito.anyString());
ReflectionTestUtils.setField(logAppender, "logEventDao", eventDao);
}
@Test
public void doAppendWithoutServerProfileTest() throws IOException {
GenericAvroConverter<BasicEndpointProfile> converter = new GenericAvroConverter<BasicEndpointProfile>(BasicEndpointProfile.SCHEMA$);
BasicEndpointProfile theLog = new BasicEndpointProfile("test");
List<LogEvent> events = new ArrayList<>();
LogEvent event1 = new LogEvent();
event1.setLogData(converter.encode(theLog));
LogEvent event2 = new LogEvent();
event2.setLogData(converter.encode(theLog));
LogEvent event3 = new LogEvent();
event3.setLogData(converter.encode(theLog));
events.add(event1);
events.add(event2);
events.add(event3);
LogSchemaDto schemaDto = new LogSchemaDto();
LogSchema schema = new LogSchema(schemaDto, BasicEndpointProfile.SCHEMA$.toString());
EndpointProfileDataDto profileDto = new EndpointProfileDataDto("1", ENDPOINT_KEY, 1, "", 0, null);
BaseLogEventPack logEventPack = new BaseLogEventPack(profileDto, DATE_CREATED, schema.getVersion(), events);
logEventPack.setLogSchema(schema);
String collectionName = (String) ReflectionTestUtils.getField(logAppender, "collectionName");
Assert.assertEquals(0, MongoDBTestRunner.getDB().getCollection(collectionName).count());
TestLogDeliveryCallback callback = new TestLogDeliveryCallback();
logAppender.doAppend(logEventPack, callback);
Assert.assertTrue(callback.success);
collectionName = (String) ReflectionTestUtils.getField(logAppender, "collectionName");
Assert.assertEquals(3, MongoDBTestRunner.getDB().getCollection(collectionName).count());
}
@Test
public void doAppendWithServerProfileTest() throws Exception {
// Reinitilize the log appender to include server profile data
this.initLogAppender(false, true);
GenericAvroConverter<BasicEndpointProfile> converter = new GenericAvroConverter<BasicEndpointProfile>(BasicEndpointProfile.SCHEMA$);
BasicEndpointProfile log = new BasicEndpointProfile("body");
List<LogEvent> logEvents = new ArrayList<>();
LogEvent alpha = new LogEvent();
alpha.setLogData(converter.encode(log));
logEvents.add(alpha);
LogEvent beta = new LogEvent();
beta.setLogData(converter.encode(log));
logEvents.add(alpha);
LogEvent gamma = new LogEvent();
gamma.setLogData(converter.encode(log));
logEvents.add(alpha);
LogSchemaDto logSchemaDto = new LogSchemaDto();
LogSchema logSchema = new LogSchema(logSchemaDto, BasicEndpointProfile.SCHEMA$.toString());
EndpointProfileDataDto profileDto = new EndpointProfileDataDto("1", ENDPOINT_KEY, 1, "", 0, null);
BaseLogEventPack logEventPack = new BaseLogEventPack(profileDto, DATE_CREATED, logSchema.getVersion(), logEvents);
logEventPack.setLogSchema(logSchema);
// Add server profile data
BaseSchemaInfo schemaInfo = new BaseSchemaInfo(Integer.toString(new Random().nextInt()), this.getResourceAsString(SERVER_PROFILE_SCHEMA_FILE));
String body = this.getResourceAsString(SERVER_PROFILE_CONTENT_FILE);
logEventPack.setServerProfile(new BaseProfileInfo(schemaInfo, body));
this.logAppender.doAppend(logEventPack, new TestLogDeliveryCallback());
String collectionName = (String) ReflectionTestUtils.getField(this.logAppender, "collectionName");
DBObject serverProfile = (DBObject) MongoDBTestRunner.getDB().getCollection(collectionName).findOne().get(SERVER_PROFILE);
DBObject profile = (DBObject) serverProfile.get("Profile");
DBObject profileNamespace = (DBObject) profile.get("org.kaaproject.kaa.schema.sample.profile");
Assert.assertEquals(SERVER_FIELD_VALUE, profileNamespace.get(SERVER_FIELD_KEY));
}
@Test
public void doAppendWithEmptyServerProfileTest() throws Exception {
// Reinitilize the log appender to include server profile data
this.initLogAppender(false, true);
GenericAvroConverter<BasicEndpointProfile> converter = new GenericAvroConverter<BasicEndpointProfile>(BasicEndpointProfile.SCHEMA$);
BasicEndpointProfile log = new BasicEndpointProfile("body");
List<LogEvent> logEvents = new ArrayList<>();
LogEvent alpha = new LogEvent();
alpha.setLogData(converter.encode(log));
logEvents.add(alpha);
LogEvent beta = new LogEvent();
beta.setLogData(converter.encode(log));
logEvents.add(alpha);
LogEvent gamma = new LogEvent();
gamma.setLogData(converter.encode(log));
logEvents.add(alpha);
LogSchemaDto logSchemaDto = new LogSchemaDto();
LogSchema logSchema = new LogSchema(logSchemaDto, BasicEndpointProfile.SCHEMA$.toString());
EndpointProfileDataDto profileDto = new EndpointProfileDataDto("1", ENDPOINT_KEY, 1, "", 0, null);
BaseLogEventPack logEventPack = new BaseLogEventPack(profileDto, DATE_CREATED, logSchema.getVersion(), logEvents);
logEventPack.setLogSchema(logSchema);
this.logAppender.doAppend(logEventPack, new TestLogDeliveryCallback());
String collectionName = (String) ReflectionTestUtils.getField(this.logAppender, "collectionName");
DBObject serverProfile = (DBObject) MongoDBTestRunner.getDB().getCollection(collectionName).findOne().get(SERVER_PROFILE);
Assert.assertEquals(null, serverProfile);
}
private void initLogAppender(boolean includeClientProfile, boolean includeServerProfile) throws Exception {
logAppender = new MongoDbLogAppender();
LogAppenderDto appenderDto = new LogAppenderDto();
appenderDto.setApplicationId(APPLICATION_ID);
appenderDto.setApplicationToken(APPLICATION_TOKEN);
appenderDto.setTenantId(TENANT_ID);
appenderDto.setHeaderStructure(Arrays.asList(LogHeaderStructureDto.values()));
String dbName = MongoDBTestRunner.getDB().getName();
List<ServerAddress> serverAddresses = MongoDBTestRunner.getDB().getMongo().getServerAddressList();
List<MongoDbServer> servers = new ArrayList<>();
for (ServerAddress serverAddress : serverAddresses) {
servers.add(new MongoDbServer(serverAddress.getHost(), serverAddress.getPort()));
}
List<MongoDBCredential> credentials = new ArrayList<>();
RawSchema rawSchema = new RawSchema(MongoDbConfig.getClassSchema().toString());
DefaultRecordGenerationAlgorithm<RawData> algotithm = new DefaultRecordGenerationAlgorithmImpl<>(rawSchema, new RawDataFactory());
RawData rawData = algotithm.getRootData();
AvroJsonConverter<MongoDbConfig> converter = new AvroJsonConverter<>(MongoDbConfig.getClassSchema(), MongoDbConfig.class);
MongoDbConfig mongoDbConfig = converter.decodeJson(rawData.getRawData());
mongoDbConfig.setMongoServers(servers);
mongoDbConfig.setMongoCredentials(credentials);
mongoDbConfig.setDbName(dbName);
mongoDbConfig.setIncludeClientProfile(includeClientProfile);
mongoDbConfig.setIncludeServerProfile(includeServerProfile);
AvroByteArrayConverter<MongoDbConfig> byteConverter = new AvroByteArrayConverter<>(MongoDbConfig.class);
byte[] rawConfiguration = byteConverter.toByteArray(mongoDbConfig);
appenderDto.setRawConfiguration(rawConfiguration);
logAppender.init(appenderDto);
}
protected String getResourceAsString(String path) throws IOException {
URL url = Thread.currentThread().getContextClassLoader().getResource(path);
File file = new File(url.getPath());
String result;
BufferedReader br = new BufferedReader(new FileReader(file));
try {
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append(System.lineSeparator());
line = br.readLine();
}
result = sb.toString();
} finally {
br.close();
}
return result;
}
private static class TestLogDeliveryCallback implements LogDeliveryCallback {
private volatile boolean success;
private volatile boolean internallError;
private volatile boolean connectionError;
private volatile boolean remoteError;
@Override
public void onSuccess() {
success = true;
}
@Override
public void onInternalError() {
internallError = true;
}
@Override
public void onConnectionError() {
connectionError = true;
}
@Override
public void onRemoteError() {
remoteError = true;
}
}
}