/*
* Copyright (c) NASK, NCSC
*
* This file is part of HoneySpider Network 2.1.
*
* This is a free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package pl.nask.hsn2.os;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.nask.hsn2.bus.connector.objectstore.ObjectStoreConnector;
import pl.nask.hsn2.bus.connector.objectstore.ObjectStoreConnectorException;
import pl.nask.hsn2.bus.connector.objectstore.ObjectStoreGetCallback;
import pl.nask.hsn2.bus.operations.Attribute;
import pl.nask.hsn2.bus.operations.ObjectData;
import pl.nask.hsn2.bus.operations.Reference;
import pl.nask.hsn2.bus.operations.builder.ObjectDataBuilder;
public final class CachingObjectStoreImpl implements ObjectStore {
private static final Logger LOG = LoggerFactory.getLogger(CachingObjectStoreImpl.class);
Map<Long, Map<String, Object>> cache = new HashMap<Long, Map<String,Object>>();
private final ObjectStoreConnector connector;
private final long jobId;
public CachingObjectStoreImpl(ObjectStoreConnector connector, long jobId) {
this.connector = connector;
this.jobId = jobId;
}
@Override
public Map<String, Object> get(long id) {
try {
Map<String, Object> result;
if (inCache(id)) {
result = getFromCache(id);
} else {
final ObjectStore os = this;
ObjectData data = connector.getObjectStoreData(jobId, id);
Map<String, Object> map = asMap(new ObjectStoreGetCallback() {
@Override
public Object handleGet(long id) {
return new OSObject(os, id);
}
}, data);
insertIntoCache(id, map);
result = map;
}
return result;
} catch (ObjectStoreConnectorException e) {
throw new RuntimeException(e);
}
}
private Map<String, Object> asMap(ObjectStoreGetCallback callback,
ObjectData obj) {
Map<String, Object> attributeMap = new HashMap<String, Object>();
for (Attribute attr : obj.getAttributes()) {
attributeMap.put(attr.getName().toLowerCase(),
valueOf(callback, attr));
}
attributeMap.put("id", obj.getId());
return attributeMap;
}
private Object valueOf(ObjectStoreGetCallback callback, Attribute attr) {
switch (attr.getType()) {
case BOOL:
return attr.getBool();
case INT:
return attr.getInteger();
case STRING:
return attr.getString();
case BYTES:
Reference ref = attr.getBytes();
Map<String, Object> mapRef = new HashMap<String, Object>();
mapRef.put("key", ref.getKey());
mapRef.put("store", ref.getStore());
return mapRef;
case FLOAT:
return attr.getFloat();
case OBJECT:
return callback.handleGet(attr.getObejectRef());
case TIME:
return attr.getTime();
case EMPTY:
default:
return null;
}
}
@Override
public List<OSObject> findByName(String attributeName) {
try {
Set<Long> ids = connector.findByAttributeName(jobId, attributeName);
return osObjects(ids);
} catch (ObjectStoreConnectorException e) {
throw new RuntimeException(e);
}
}
@Override
public List<OSObject> findByValue(String attributeName, Object value) {
Set<Long> ids;
try {
if (value instanceof String) {
ids = connector.findByAttributeValue(jobId, attributeName, (String) value);
} else if (value instanceof Boolean) {
ids = connector.findByAttributeValue(jobId, attributeName, (Boolean) value);
} else if (value instanceof Long) {
ids = connector.findByAttributeValue(jobId, attributeName, (Long) value);
} else if (value instanceof OSObject) {
ids = connector.findByObjectId(jobId, attributeName, ((OSObject) value).getId());
} else if (value instanceof Integer) {
ids = connector.findByAttributeValue(jobId, attributeName, (Integer) value);
} else {
throw new RuntimeException("Unknown object type for: " + value);
}
return osObjects(ids);
} catch (ObjectStoreConnectorException e) {
throw new RuntimeException(e);
}
}
@Override
public void addAttribute(long id, String key, Object value) {
Map<String, Object> obj = get(id);
obj.put(key, value);
try {
Attribute attribute = new Attribute(key);
if (value instanceof Reference) {
attribute.setDataRef((Reference) value);
} else if (value instanceof OSObject){
attribute.setObjectRef(((OSObject)value).getId());
} else {
attribute.setSimpleValue(value);
}
connector.updateObjectStoreData(jobId, Arrays.asList(
new ObjectDataBuilder().setId(id).addAttribute(attribute).build()));
} catch (ObjectStoreConnectorException e) {
LOG.error("Failed to update data in the ObjectStore (jobId={}, dataId={}, attrName={}, value={}, responseType={}, responseMsg={})",
new Object[]{jobId, id, key, value,e.getResponseType(), e.getError()});
throw new IllegalStateException(e);
}
}
private void insertIntoCache(long id, Map<String, Object> map) {
cache.put(id, map);
}
private Map<String, Object> getFromCache(long id) {
return cache.get(id);
}
private boolean inCache(long id) {
return cache.containsKey(id);
}
private List<OSObject> osObjects(Set<Long> ids) {
List<OSObject> list = new ArrayList<OSObject>(ids.size());
for (Long id: ids) {
list.add(new OSObject(this, id));
}
return list;
}
}