//
// Copyright 2010 Cinch Logic Pty Ltd.
//
// http://www.chililog.com
//
// 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.chililog.server.data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.regex.Pattern;
import org.chililog.server.common.ChiliLogException;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* <p>
* This class contains meta-data that describes a repository:
* <ul>
* <li>General Details - Name and status</li>
* <li>Security - authentication information for publisher and subscribers of the repository</li>
* <li>Storage - how to store log entries in the database</li>
* <li>Parsers - how information is to be extracted from log entries and stored in the database</li>
* </ul>
* </p>
* <p>
* HornetQ/Repository/JMS PubSub concepts:
* <ul>
* <li>A repository maps to a HornetQ address. A HornetQ address is the same as a JMS Topic.</li>
* <li>Publishers send or produce log entries to a repository (HornetQ address)</li>
* <li>Subscribers consume messages from the repository (HornetQ address)</li>
* <li>Each subscriber is issued with its own HornetQ queue that is bound to the repository's HornetQ address.</li>
* </ul>
* </p>
* <p>
* Note issue with slow consumers in a pub/sub model.Messages are stored once in the address and references passed into
* queues bound to that address. If there is a slow queue, un-consumed messages will not be cleared from memory.
* http://docs.jboss.org/hornetq/2.2.2.Final/user-manual/en/html_single/index.html#d0e5059
* </p>
* <p>
* Paging for a HornetQ address. See
* http://docs.jboss.org/hornetq/2.2.2.Final/user-manual/en/html_single/index.html#paging.
* </p>
* <p>
* Journal is also implemented individually for an address. See
* http://docs.jboss.org/hornetq/2.2.2.Final/user-manual/en/html_single/index.html#persistence.
* </p>
*
* @author vibul
*
*/
public class RepositoryConfigBO extends BO implements Serializable {
private static final long serialVersionUID = 1L;
private static Pattern _namePattern = Pattern.compile("^[a-z0-9_]+$");
private String _name;
private String _displayName;
private String _description;
private Status _startupStatus = Status.ONLINE;
private boolean _storeEntriesIndicator = false;
private boolean _storageQueueDurableIndicator = false;
private long _storageQueueWorkerCount = 1;
private long _storageMaxKeywords = UNLIMITED_MAX_KEYWORDS;
private long _maxMemory = 1024 * 1024 * 20; // 20 MB
private MaxMemoryPolicy _maxMemoryPolicy = MaxMemoryPolicy.PAGE;
private long _pageSize = 1024 * 1024 * 10; // 10 MB
private long _pageCountCache = 3; // max 3 pages in memory when paging
private ArrayList<RepositoryParserConfigBO> _parsers = new ArrayList<RepositoryParserConfigBO>();
static final String NAME_FIELD_NAME = "name";
static final String DISPLAY_NAME_FIELD_NAME = "display_name";
static final String DESCRIPTION_FIELD_NAME = "description";
static final String STARTUP_STATUS_FIELD_NAME = "startup_status";
static final String STORE_ENTRIES_INDICATOR_FIELD_NAME = "store_entries_indicator";
static final String STORAGE_QUEUE_DURABLE_INDICATOR_FIELD_NAME = "storage_queue_durable_indicator";
static final String STORAGE_QUEUE_WORKER_COUNT_FIELD_NAME = "storage_queue_worker_count";
static final String STROAGE_MAX_KEYWORDS = "storage_max_keywords";
public static final long UNLIMITED_MAX_KEYWORDS = -1;
static final String MAX_MEMORY_FIELD_NAME = "max_memory";
static final String MAX_MEMORY_POLICY_FIELD_NAME = "max_memory_policy";
static final String PAGE_SIZE_FIELD_NAME = "page_size";
static final String PAGE_COUNT_CACHE_FIELD_NAME = "page_count_cache";
static final String PARSERS_FIELD_NAME = "parsers";
/**
* Basic constructor
*/
public RepositoryConfigBO() {
return;
}
/**
* Constructor that loads our properties retrieved from the mongoDB dbObject
*
* @param dbObject
* database object as retrieved from mongoDB
* @throws ChiliLogException
*/
public RepositoryConfigBO(DBObject dbObject) throws ChiliLogException {
super(dbObject);
// General
_name = MongoUtils.getString(dbObject, NAME_FIELD_NAME, true);
_displayName = MongoUtils.getString(dbObject, DISPLAY_NAME_FIELD_NAME, false);
_description = MongoUtils.getString(dbObject, DESCRIPTION_FIELD_NAME, false);
_startupStatus = Status.valueOf(MongoUtils.getString(dbObject, STARTUP_STATUS_FIELD_NAME, true));
// PubSub settings
_maxMemory = MongoUtils.getLong(dbObject, MAX_MEMORY_FIELD_NAME, true);
_maxMemoryPolicy = MaxMemoryPolicy.valueOf(MongoUtils.getString(dbObject, MAX_MEMORY_POLICY_FIELD_NAME, true));
_pageSize = MongoUtils.getLong(dbObject, PAGE_SIZE_FIELD_NAME, true);
_pageCountCache = MongoUtils.getLong(dbObject, PAGE_COUNT_CACHE_FIELD_NAME, true);
// Storage
_storeEntriesIndicator = MongoUtils.getBoolean(dbObject, STORE_ENTRIES_INDICATOR_FIELD_NAME, true);
_storageQueueDurableIndicator = MongoUtils.getBoolean(dbObject, STORAGE_QUEUE_DURABLE_INDICATOR_FIELD_NAME,
true);
_storageQueueWorkerCount = MongoUtils.getLong(dbObject, STORAGE_QUEUE_WORKER_COUNT_FIELD_NAME, true);
_storageMaxKeywords = MongoUtils.getLong(dbObject, STROAGE_MAX_KEYWORDS, true);
// Parser
BasicDBList list = (BasicDBList) dbObject.get(PARSERS_FIELD_NAME);
ArrayList<RepositoryParserConfigBO> parserList = new ArrayList<RepositoryParserConfigBO>();
if (list != null && list.size() > 0) {
for (Object item : list) {
RepositoryParserConfigBO field = new RepositoryParserConfigBO((DBObject) item);
parserList.add(field);
}
}
_parsers = parserList;
return;
}
/**
* Puts our properties into the mongoDB object so that it can be saved
*
* @param dbObject
* mongoDB database object that can be used for saving
* @throws ChiliLogException
*/
@Override
protected void savePropertiesToDBObject(DBObject dbObject) throws ChiliLogException {
MongoUtils.setString(dbObject, NAME_FIELD_NAME, _name, true);
// Check name format
if (!_namePattern.matcher(_name).matches()) {
throw new ChiliLogException(Strings.REPO_INFO_NAME_FORMAT_ERROR, _name);
}
// Check that page file size is less than max memory
if (_pageSize > _maxMemory) {
throw new ChiliLogException(Strings.REPO_INFO_PAGE_FILE_SIZE_ERROR, _pageSize, _maxMemory);
}
// General
MongoUtils.setString(dbObject, DISPLAY_NAME_FIELD_NAME, _displayName, false);
MongoUtils.setString(dbObject, DESCRIPTION_FIELD_NAME, _description, false);
MongoUtils.setString(dbObject, STARTUP_STATUS_FIELD_NAME, _startupStatus.toString(), true);
// PubSub settings
MongoUtils.setLong(dbObject, MAX_MEMORY_FIELD_NAME, _maxMemory, true);
MongoUtils.setString(dbObject, MAX_MEMORY_POLICY_FIELD_NAME, _maxMemoryPolicy.toString(), true);
MongoUtils.setLong(dbObject, PAGE_SIZE_FIELD_NAME, _pageSize, true);
MongoUtils.setLong(dbObject, PAGE_COUNT_CACHE_FIELD_NAME, _pageCountCache, true);
// Storage
MongoUtils.setBoolean(dbObject, STORE_ENTRIES_INDICATOR_FIELD_NAME, _storeEntriesIndicator, true);
MongoUtils
.setBoolean(dbObject, STORAGE_QUEUE_DURABLE_INDICATOR_FIELD_NAME, _storageQueueDurableIndicator, true);
MongoUtils.setLong(dbObject, STORAGE_QUEUE_WORKER_COUNT_FIELD_NAME, _storageQueueWorkerCount, true);
MongoUtils.setLong(dbObject, STROAGE_MAX_KEYWORDS, _storageMaxKeywords, true);
// Parsers
ArrayList<DBObject> fieldList = new ArrayList<DBObject>();
for (RepositoryParserConfigBO parser : _parsers) {
BasicDBObject obj = new BasicDBObject();
parser.savePropertiesToDBObject(obj);
fieldList.add(obj);
}
dbObject.put(PARSERS_FIELD_NAME, fieldList);
}
/**
* <p>
* Returns the unique name for this repository. This name forms part the mongoDB collection for storing data for
* this repository.
* </p>
* <p>
* If the name is "xxx", then the mongoDB collection name is "xxx_repository"
* </p>
*/
public String getName() {
return _name;
}
public void setName(String name) {
_name = name;
}
/**
* Returns the name of the collection in mongoDB where repository entries will be stored.
*/
public String getMongoDBCollectionName() {
return String.format("repo_%s", _name);
}
/**
* Returns user friendly display name for this repository
*/
public String getDisplayName() {
return _displayName;
}
public void setDisplayName(String displayName) {
_displayName = displayName;
}
/**
* Returns the description for this repository
*/
public String getDescription() {
return _description;
}
public void setDescription(String description) {
_description = description;
}
/**
* Returns the status of the repository
*/
public Status getStartupStatus() {
return _startupStatus;
}
public void setStartupStatus(Status status) {
_startupStatus = status;
}
/**
* Returns a list fields that is to be parsed and stored in this repository
*/
public ArrayList<RepositoryParserConfigBO> getParsers() {
return _parsers;
}
/**
* Returns the address for publishers to use for sending in log entries
*/
public String getPubSubAddress() {
return buildPubSubAddress(_name);
}
/**
* Creates the HornetQ address for a repository of the specified name
*
* @param repositoryName
* Name of the repository
* @return HornetQ name of the repository
*/
public static String buildPubSubAddress(String repositoryName) {
return String.format("repo.%s", repositoryName);
}
/**
* Returns the address of the message queue that used for storing incoming log entries
*/
public String getStorageQueueName() {
return String.format("repo.%s.storage", _name);
}
/**
* Returns the name of the role that has all access to this repository
*/
public String getAdministratorRoleName() {
return UserBO.createRepositoryAdministratorRoleName(_name);
}
/**
* Returns the name of the role that can use the workbench to access the data in this repository
*/
public String getWorkbenchRoleName() {
return UserBO.createRepositoryWorkbenchRoleName(_name);
}
/**
* Returns the name of the role that can publish (write) log entries to this repository
*/
public String getPublisherRoleName() {
return UserBO.createRepositoryPublisherRoleName(_name);
}
/**
* Returns the name of the role that can subscribe to (read) this repository
*/
public String getSubscriberRoleName() {
return UserBO.createRepositorySubscriberRoleName(_name);
}
/**
* Returns a flag indicating if the log entries published to this repository is to be stored in the database.
*/
public boolean getStoreEntriesIndicator() {
return _storeEntriesIndicator;
}
public void setStoreEntriesIndicator(boolean storeEntriesIndicator) {
_storeEntriesIndicator = storeEntriesIndicator;
}
/**
* <p>
* Returns a flag indicating if the storage queue for this repository is to be durable. For this to take effect, the
* app.properties mq.persistence_enabled must also be set to true.
* </p>
* <p>
* A durable queue saves all entries to hard disk. This means that if the server crashes, queued log entries are not
* lost. However, storing queued log entries slows performance.
* </p>
* <p>
* By default, storage queues are not durable. We recommend that you only make storage queues durable if the log
* entries contain critical information that cannot be lost.
* </p>
*/
public boolean getStorageQueueDurableIndicator() {
return _storageQueueDurableIndicator;
}
public void setStorageQueueDurableIndicator(boolean storageQueueDurableIndicator) {
_storageQueueDurableIndicator = storageQueueDurableIndicator;
}
/**
* <p>
* Returns the number of writer worker threads that will be created to processing queued incoming log entries. The
* more workers, the quicker log entries are saved to the database and the smaller the storage queue size.
* </p>
* <p>
* The default is 1.
* </p>
*/
public long getStorageQueueWorkerCount() {
return _storageQueueWorkerCount;
}
public void setStorageQueueWorkerCount(long writeWorkerCount) {
_storageQueueWorkerCount = writeWorkerCount;
}
/**
* The maximum amount of memory (in bytes) that will be used by the storage queue. <code>-1</code> means no limit.
*/
public long getMaxMemory() {
return _maxMemory;
}
public void setMaxMemory(long maxMemory) {
_maxMemory = maxMemory;
}
/**
* Determines what happens when MaxMemory is reached on the storage queue.
*/
public MaxMemoryPolicy getMaxMemoryPolicy() {
return _maxMemoryPolicy;
}
public void setMaxMemoryPolicy(MaxMemoryPolicy maxMemoryPolicy) {
_maxMemoryPolicy = maxMemoryPolicy;
}
/**
* If MaxMemoryPolicy is set to PAGE, then this value determines the size of each page file on the hard disk in
* bytes.
*/
public long getPageSize() {
return _pageSize;
}
public void setPageSize(long pageSize) {
_pageSize = pageSize;
}
/**
* If MaxMemoryPolicy is set to PAGE, then this value determines the number of pages to be kept in memory during
* page navigation.
*/
public long getPageCountCache() {
return _pageCountCache;
}
public void setPageCountCache(long writeQueuePageCountCache) {
_pageCountCache = writeQueuePageCountCache;
}
/**
* Returns the maximum number of keywords to store.
*/
public long getStorageMaxKeywords() {
return _storageMaxKeywords;
}
public void setStorageMaxKeywords(long parserMaxKeywords) {
_storageMaxKeywords = parserMaxKeywords;
}
/**
* Repository status
*
* @author vibul
*
*/
public enum Status {
/**
* Users with permission can read from and write to this repository
*/
ONLINE,
/**
* Users with permission can read from this repository.
*/
READONLY,
/**
* Nobody can read from or write to this repository
*/
OFFLINE
}
/**
* Policy to follow once a queue's max memory is reached
*
* @author vibul
*
*/
public enum MaxMemoryPolicy {
/**
* Messages will be pushed to page files on the hard disk once queue memory limit is reached.
*/
PAGE,
/**
* New messages will be dropped once queue memory limit is reached.
*/
DROP,
/**
* Force producers to block and wait before new messages can be sent once queue memory limit is reached.
*/
BLOCK
}
}