/*
* 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.oraclenosql.appender;
import oracle.kv.Direction;
import oracle.kv.DurabilityException;
import oracle.kv.FaultException;
import oracle.kv.KVStore;
import oracle.kv.KVStoreConfig;
import oracle.kv.KVStoreFactory;
import oracle.kv.Key;
import oracle.kv.OperationExecutionException;
import oracle.kv.util.kvlite.KVLite;
import org.apache.avro.generic.GenericRecord;
import org.apache.commons.io.FileUtils;
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.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.oraclenosql.config.gen.KvStoreNode;
import org.kaaproject.kaa.server.appenders.oraclenosql.config.gen.OracleNoSqlConfig;
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.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.util.ReflectionTestUtils;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class OracleNoSqlLogAppenderTest {
private static final Logger LOG = LoggerFactory.getLogger(OracleNoSqlLogAppenderTest.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 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 STORE_NAME = "kvstore";
private static final String STORE_HOST = "127.0.0.1";
private static final int STORE_PORT = 10555;
private static KVLite kvLite;
private static File storeRootDir;
private LogAppender logAppender;
@BeforeClass
public static void init() throws Exception {
File tempDir = new File(System.getProperty("java.io.tmpdir"));
storeRootDir = new File(tempDir, "kvRoot-test");
if (storeRootDir.exists()) {
FileUtils.deleteQuietly(storeRootDir);
}
storeRootDir.mkdirs();
kvLite = new KVLite(storeRootDir.getAbsolutePath(), STORE_NAME, STORE_PORT, 0, STORE_HOST, null, null,
KVLite.DEFAULT_NUM_PARTITIONS, null, true);
kvLite.start();
}
@AfterClass
public static void afterClass() throws Exception {
if (kvLite != null) {
kvLite.shutdownStore(true);
kvLite.stop(true);
kvLite = null;
}
if (storeRootDir != null) {
FileUtils.deleteQuietly(storeRootDir);
storeRootDir = null;
}
}
private static KVStore connectToStore() {
KVStoreConfig config = new KVStoreConfig(STORE_NAME, STORE_HOST + ":" + STORE_PORT);
return KVStoreFactory.getStore(config);
}
@After
public void after() throws Exception {
LOG.info("Deleting data from Oracle No SQL database");
KVStore kvStore = connectToStore();
if (kvStore != null) {
Iterator<Key> it = kvStore.storeKeysIterator(Direction.UNORDERED, 100, null, null, null);
int numdeleted = 0;
while (it.hasNext()) {
if (kvStore.delete(it.next())) {
numdeleted++;
}
}
LOG.info("Deleted {} key/value pairs", numdeleted);
}
kvStore.close();
}
@Before
public void beforeTest() throws IOException {
logAppender = new OracleNoSqlLogAppender();
LogAppenderDto appenderDto = new LogAppenderDto();
appenderDto.setApplicationId(APPLICATION_ID);
appenderDto.setApplicationToken(APPLICATION_TOKEN);
appenderDto.setTenantId(TENANT_ID);
appenderDto.setHeaderStructure(Arrays.asList(LogHeaderStructureDto.values()));
List<KvStoreNode> nodes = Arrays.asList(new KvStoreNode(STORE_HOST, STORE_PORT));
OracleNoSqlConfig oracleConfig = new OracleNoSqlConfig();
oracleConfig.setKvStoreNodes(nodes);
oracleConfig.setStoreName(STORE_NAME);
AvroByteArrayConverter<OracleNoSqlConfig> converter = new AvroByteArrayConverter<>(OracleNoSqlConfig.class);
byte[] rawConfiguration = converter.toByteArray(oracleConfig);
appenderDto.setRawConfiguration(rawConfiguration);
logAppender.setApplicationToken(appenderDto.getApplicationToken());
logAppender.init(appenderDto);
}
@Test
public void closeAppenderTest() {
Assert.assertFalse((boolean) ReflectionTestUtils.getField(logAppender, "closed"));
logAppender.close();
Assert.assertTrue((boolean) ReflectionTestUtils.getField(logAppender, "closed"));
}
@Test
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();
LogSchemaDto schemaDto = new LogSchemaDto();
LogSchema schema = new LogSchema(schemaDto, BasicEndpointProfile.SCHEMA$.toString());
EndpointProfileDataDto profileDto = new EndpointProfileDataDto("1", ENDPOINT_KEY, 1, "test", 0, null);
BaseLogEventPack logEventPack = new BaseLogEventPack(profileDto, DATE_CREATED, schema.getVersion(), null);
logEventPack.setLogSchema(schema);
logAppender.doAppend(logEventPack, callback);
Assert.assertTrue(callback.internallError);
}
@Test
public void doAppendWithCatchIOExceptionTest() throws NoSuchFieldException, SecurityException, IllegalArgumentException,
IllegalAccessException, DurabilityException, OperationExecutionException, FaultException, 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, "test", 0, null);
BaseLogEventPack logEventPack = new BaseLogEventPack(profileDto, DATE_CREATED, schema.getVersion(), events);
logEventPack.setLogSchema(schema);
TestLogDeliveryCallback callback = new TestLogDeliveryCallback();
logAppender.doAppend(logEventPack, callback);
Assert.assertTrue(callback.internallError);
}
@Test
public void doAppendTest() throws Exception {
List<LogEvent> events = new ArrayList<>();
LogEvent event1 = new LogEvent();
event1.setLogData(LOG_DATA.getBytes(UTF_8));
LogEvent event2 = new LogEvent();
event2.setLogData(LOG_DATA.getBytes(UTF_8));
LogEvent event3 = new LogEvent();
event3.setLogData(LOG_DATA.getBytes(UTF_8));
events.add(event1);
events.add(event2);
events.add(event3);
LogSchemaDto dto = new LogSchemaDto();
dto.setVersion(1);
LogSchema schema = new LogSchema(dto, EMPTY_SCHEMA);
int version = dto.getVersion();
EndpointProfileDataDto profileDto = new EndpointProfileDataDto("1", ENDPOINT_KEY, 1, "test", 0, null);
BaseLogEventPack logEventPack = new BaseLogEventPack(profileDto, DATE_CREATED, version, events);
logEventPack.setLogSchema(schema);
Map<String, GenericAvroConverter<GenericRecord>> converters = new HashMap<>();
GenericAvroConverter<GenericRecord> converter = new GenericAvroConverter<GenericRecord>(schema.getSchema()) {
@Override
public GenericRecord decodeBinary(byte[] bytes) {
return null;
}
@Override
public String encodeToJson(GenericRecord record) {
return LOG_DATA;
}
};
converters.put(schema.getSchema(), converter);
ReflectionTestUtils.setField(logAppender, "converters", converters);
Assert.assertEquals(0, getKeyValuesCount());
TestLogDeliveryCallback callback = new TestLogDeliveryCallback();
logAppender.doAppend(logEventPack, callback);
Assert.assertTrue(callback.success);
Assert.assertEquals(3, getKeyValuesCount());
}
public int getKeyValuesCount() throws Exception {
int numvalues = 0;
KVStore kvStore = connectToStore();
if (kvStore != null) {
Iterator<Key> it = kvStore.storeKeysIterator(Direction.UNORDERED, 100, null, null, null);
while (it.hasNext()) {
it.next();
numvalues++;
}
}
kvStore.close();
return numvalues;
}
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;
}
}
}