/*******************************************************************************
* Copyright (c) 2010 Weltevree Beheer BV, Remain Software & Industrial-TSI
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wim Jongman - initial API and implementation
*
*
*******************************************************************************/
package org.eclipse.ecf.protocol.nntp.store.derby.internal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import org.eclipse.ecf.protocol.nntp.core.DateParser;
import org.eclipse.ecf.protocol.nntp.core.Debug;
import org.eclipse.ecf.protocol.nntp.core.StringUtils;
import org.eclipse.ecf.protocol.nntp.model.IArticle;
import org.eclipse.ecf.protocol.nntp.model.INewsgroup;
import org.eclipse.ecf.protocol.nntp.model.ISecureStore;
import org.eclipse.ecf.protocol.nntp.model.IServer;
import org.eclipse.ecf.protocol.nntp.model.IStore;
import org.eclipse.ecf.protocol.nntp.model.IStoreEvent;
import org.eclipse.ecf.protocol.nntp.model.IStoreEventListener;
import org.eclipse.ecf.protocol.nntp.model.NNTPConnectException;
import org.eclipse.ecf.protocol.nntp.model.NNTPException;
import org.eclipse.ecf.protocol.nntp.model.NNTPIOException;
import org.eclipse.ecf.protocol.nntp.model.SALVO;
import org.eclipse.ecf.protocol.nntp.model.StoreEvent;
import org.eclipse.ecf.protocol.nntp.model.StoreException;
/**
* This implementation of IStore uses the Java file system to store the
* newsgroup data.
*
* @author Wim Jongman
*
*/
public class Store implements IStore {
/**
* Stores the last exception.
*/
private HashMap listeners;
private String root = "";
private ISecureStore secureStore;
private Database database;
private ServerDAO serverDOA;
private NewsgroupDAO groupDOA;
private ArticleDAO articleDOA;
public Store(String root) throws StoreException {
if (!root.endsWith(SALVO.SEPARATOR))
this.root = root + SALVO.SEPARATOR;
boolean init = System
.getProperty("org.eclipse.ecf.protocol.nntp.store.derby.init") != null;
initDB(this.root, init);
}
/**
* Initializes the database.
*
* @param root
* the physical location of the database
* @param initialize
* is true if you want to drop all existing data
* @throws StoreException
*/
public void initDB(String root, boolean initialize) throws StoreException {
try {
setDatabase(Database.createDatabase(root, initialize));
serverDOA = new ServerDAO(getDatabase().getConnection(), this);
groupDOA = new NewsgroupDAO(getDatabase().getConnection());
articleDOA = new ArticleDAO(getDatabase().getConnection());
} catch (Exception e) {
throw new StoreException("Error occured intializing store ", e);
}
}
/**
* Deletes the database.
*
* @throws StoreException
*/
public void dropDB() throws StoreException {
try {
getDatabase().closeDB();
getDatabase().dropDB();
} catch (Exception e) {
throw new StoreException("Error occured dropping store ", e);
}
}
public void subscribeServer(final IServer server, final String passWord)
throws StoreException {
getSecureStore().put(server.getAddress(), passWord, true);
server.setSubscribed(true);
serverDOA.insertServer(server);
fireEvent(new StoreEvent(server, SALVO.EVENT_ADD_SERVER));
}
public ISecureStore getSecureStore() {
if (secureStore == null) {
secureStore = new ISecureStore() {
HashMap memory = new HashMap();
public void remove(String key) {
memory.remove(key);
}
public void put(String key, String value, boolean encrypt) {
memory.put(key, value);
}
public String get(String key, String def) {
return (String) (memory.get(key) == null ? def : memory
.get(key));
}
public void clear() {
memory.clear();
}
};
}
return secureStore;
}
// public Exception getLastException() {
// return lastException;
// }
public void addListener(IStoreEventListener listener, int eventType) {
if (listeners == null) {
listeners = new HashMap();
}
synchronized (listeners) {
ArrayList list = (ArrayList) listeners.get(new Integer(eventType));
if (list == null) {
list = new ArrayList();
listeners.put(new Integer(eventType), list);
}
if (!list.contains(listener))
list.add(listener);
}
}
private void fireEvent(IStoreEvent event) {
if (listeners == null || listeners.isEmpty())
return;
synchronized (listeners) {
Set keys = listeners.keySet();
for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
Integer key = (Integer) iterator.next();
if ((event.getEventType() | key.intValue()) == key.intValue()) {
Debug.log(getClass(), "Listeners found for event type "
+ key.intValue() + " (" + event.getEventType()
+ "): ");
ArrayList list = (ArrayList) listeners.get(key);
for (Iterator iterator2 = list.iterator(); iterator2
.hasNext();) {
IStoreEventListener listener = (IStoreEventListener) iterator2
.next();
Debug.log(getClass(), "Calling Listener "
+ listener.getClass().getName());
listener.storeEvent(event);
}
}
}
}
}
public void removeListener(IStoreEventListener listener) {
if (listeners == null || listeners.isEmpty())
return;
Set keySet = listeners.keySet();
for (Iterator iterator = keySet.iterator(); iterator.hasNext();) {
Integer key = (Integer) iterator.next();
ArrayList list = (ArrayList) listeners.get(key);
if (list.contains(listener)) {
list.remove(listener);
if (list.isEmpty()) {
listeners.remove(key);
}
}
}
}
public void unsubscribeNewsgroup(INewsgroup group, boolean permanent)
throws StoreException {
if (permanent) {
groupDOA.deleteNewsgroup(group);
fireEvent(new StoreEvent(group, SALVO.EVENT_REMOVE_GROUP));
} else {
group.setSubscribed(false);
groupDOA.updateNewsgroup(group);
fireEvent(new StoreEvent(group, SALVO.EVENT_UNSUBSCRIBE_GROUP));
}
}
public void unsubscribeServer(IServer server, boolean permanent)
throws StoreException {
if (permanent) {
serverDOA.deleteServer(server);
fireEvent(new StoreEvent(server, SALVO.EVENT_REMOVE_SERVER));
} else {
server.setSubscribed(false);
serverDOA.updateServer(server);
fireEvent(new StoreEvent(server, SALVO.EVENT_UNSUBSCRIBE_SERVER));
}
}
public void subscribeNewsgroup(INewsgroup group) throws StoreException {
INewsgroup oldGroup = groupDOA.getNewsgroup(group);
group.setSubscribed(true);
if (oldGroup == null) {
groupDOA.insertNewsgroup(group);
fireEvent(new StoreEvent(group, SALVO.EVENT_ADD_GROUP));
fireEvent(new StoreEvent(group, SALVO.EVENT_SUBSCRIBE_GROUP));
} else {
group.setProperty("DB_ID", oldGroup.getProperty("DB_ID"));
groupDOA.updateNewsgroup(group);
fireEvent(new StoreEvent(group, SALVO.EVENT_SUBSCRIBE_GROUP));
}
}
public INewsgroup[] getSubscribedNewsgroups(IServer server)
throws StoreException {
return groupDOA.getSubscribedNewsgroups(server, true);
}
public void updateAttributes(INewsgroup newsgroup) throws StoreException {
Debug.log(getClass(), "Updating attributes of group " + newsgroup);
groupDOA.updateNewsgroup(newsgroup);
fireEvent(new StoreEvent(newsgroup, SALVO.EVENT_CHANGE_GROUP));
}
public IServer[] getServers() throws NNTPException {
IServer[] subsc = serverDOA.getServers(true);
IServer[] unsubsc = serverDOA.getServers(false);
IServer[] result = new IServer[subsc.length + unsubsc.length];
int counter = 0;
for (int i = 0; i < subsc.length; i++) {
result[counter++] = subsc[i];
}
for (int i = 0; i < unsubsc.length; i++) {
result[counter++] = unsubsc[i];
}
return result;
}
public IArticle[] getArticles(INewsgroup newsgroup, int from, int to)
throws StoreException {
if (getLastArticle(newsgroup) == null
|| getLastArticle(newsgroup).getArticleNumber() < to)
return null;
return articleDOA.getArticles(newsgroup, from, to);
}
/**
* Gets the article with this number from the newsgroup store.
*
* @param newsgroup
* @param number
* @return the article or null if it does not exist
* @throws StoreException
*/
public IArticle getArticle(INewsgroup newsgroup, int number)
throws StoreException {
IArticle[] article = articleDOA.getArticles(newsgroup, number, number);
if (article.length == 1)
return article[0];
else
return null;
}
/**
* Does what storeArticles should do. This was internalized so that it can
* also be called by update methods.
*
* @param articles
* @return false if not success else true
* @throws StoreException
*/
private void internalStoreArticles(Collection articles)
throws StoreException {
long t = System.currentTimeMillis();
for (Iterator iterator = articles.iterator(); iterator.hasNext();) {
IArticle article = (IArticle) iterator.next();
if (!articleDOA.hasArticle(article)) {
articleDOA.insertArticle(article);
}
}
System.out.println((System.currentTimeMillis() - t) / 1000);
}
public IArticle getFirstArticle(INewsgroup newsgroup) throws StoreException {
return articleDOA.getOldestArticle(newsgroup);
}
public IArticle getLastArticle(INewsgroup newsgroup) throws StoreException {
return articleDOA.getNewestArticle(newsgroup);
}
public String[] getArticleBody(IArticle article) throws StoreException {
return articleDOA.getArticleBody(article);
}
public void storeArticleBody(IArticle article, String[] body)
throws StoreException {
articleDOA.deleteArticleBody(article);
articleDOA.insertArticleBody(article, body);
}
public IArticle[] getFollowUps(IArticle article) throws StoreException {
return articleDOA.getFollowUps(article);
}
public void updateArticle(IArticle article) throws StoreException {
articleDOA.updateArticle(article);
fireEvent(new StoreEvent(article, SALVO.EVENT_CHANGE_GROUP));
}
public String getDescription() {
return "Derby Storage";
}
public void storeArticles(IArticle[] articles) throws StoreException {
internalStoreArticles(Arrays.asList(articles));
fireEvent(new StoreEvent(articles, SALVO.EVENT_CHANGE_GROUP));
}
public void setWaterMarks(INewsgroup newsgroup) throws NNTPIOException,
NNTPConnectException, StoreException {
groupDOA.updateNewsgroup(newsgroup);
fireEvent(new StoreEvent(newsgroup, SALVO.EVENT_CHANGE_GROUP));
}
public void setSecureStore(ISecureStore secureStore) {
this.secureStore = secureStore;
}
public IArticle getArticle(String URL) throws NNTPException {
String groupName = StringUtils.split(
StringUtils.split(URL, "&article")[0], "=")[1];
String serverURL = StringUtils.split(URL, "/?group")[0];
IServer server = serverDOA.getServer(serverURL)[0];
INewsgroup group = groupDOA.getNewsgroup(server, groupName)[0];
return articleDOA.getArticle(group, URL);
}
public int purge(Calendar date, int number) throws NNTPIOException {
int result = 0;
try {
IServer[] servers = getServers();
for (int i = 0; i < servers.length; i++) {
INewsgroup[] groups = getSubscribedNewsgroups(servers[i]);
for (int j = 0; j < groups.length; j++) {
IArticle candidate = getFirstArticle(groups[j]);
while (candidate != null) {
Date postDate = DateParser.parseDate(candidate
.getDate());
if (postDate == null) {
result += delete(candidate);
}
else if (postDate.before(date.getTime())) {
result += delete(candidate);
}
if (number > 0 && result == number)
return result;
candidate = getFirstArticle(groups[j]);
}
}
}
} catch (NNTPException e) {
throw new NNTPIOException("Error during purge", e);
}
return result;
}
public int delete(IArticle article) throws NNTPIOException {
try {
int result = 0;
IArticle[] followUps = getFollowUps(article);
for (int i = 0; i < followUps.length; i++) {
IArticle followUp = followUps[i];
result = result + delete(followUp);
}
articleDOA.deleteArticle(article);
fireEvent(new StoreEvent(article, SALVO.EVENT_REMOVE_ARTICLE));
return ++result;
} catch (StoreException e) {
throw new NNTPIOException(e.getMessage(), e);
}
}
private void setDatabase(Database database) {
this.database = database;
}
public Database getDatabase() {
return database;
}
@Override
public int getListenerCount() {
if (listeners == null)
return 0;
return listeners.size();
}
/**
* Get articles of a particular user
*
* @param newsgroup
* Newsgroup
* @param userId
* Full username
* @return articles of a particular user in the particular Newsgroup
*
*/
public IArticle[] getArticlesByUserId(INewsgroup newsgroup, String userId) {
ArrayList<IArticle> result = new ArrayList<IArticle>();
try {
Integer[] articleIds = articleDOA.getArticleIdsFromUser(userId);
for (int i : articleIds) {
IArticle article = articleDOA.getArticleById(newsgroup, i);
if (article != null) {
result.add(article);
}
}
return (IArticle[]) result.toArray(new IArticle[0]);
} catch (StoreException e) {
Debug.log(getClass(), e);
}
return null;
}
/**
*
* Get marked articles for a particular newsgroup
*
* @param newsgroup
* Newsgroup
* @return marked articles for a particular newsgroup
*/
public IArticle[] getMarkedArticles(INewsgroup newsgroup) {
try {
return articleDOA.getMarkedArticles(newsgroup);
} catch (StoreException e) {
Debug.log(getClass(), e);
}
return null;
}
/**
*
* Get all marked articles
*
* @return marked articles for all newsgroups
*/
public IArticle[] getAllMarkedArticles(IServer server) {
try {
INewsgroup[] newsgroups = getSubscribedNewsgroups(server);
ArrayList<IArticle> result = new ArrayList<IArticle>();
for (INewsgroup newsgroup : newsgroups) {
IArticle[] markedArticlesForANewsgroup = articleDOA
.getMarkedArticles(newsgroup);
for (IArticle article : markedArticlesForANewsgroup) {
result.add(article);
}
}
return (IArticle[]) result.toArray(new IArticle[0]);
} catch (StoreException e) {
Debug.log(getClass(), e);
}
return null;
}
/**
* Get article from messageId
*
* @param newsgroup
* Newsgroup
* @param msgId message Id of article
* @return article which has the particular message id
*
*/
public IArticle getArticleByMsgId(INewsgroup newsgroup, String msgId) {
try {
return articleDOA.getArticleByMsgId(newsgroup, msgId);
} catch (StoreException e) {
Debug.log(getClass(), e);
}
return null;
}
}