/*
* Copyright 2013
*
* 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.openntf.domino.impl;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import lotus.domino.NotesException;
import org.openntf.domino.Database;
import org.openntf.domino.Session;
import org.openntf.domino.WrapperFactory;
import org.openntf.domino.annotations.Legacy;
import org.openntf.domino.ext.Session.Fixes;
import org.openntf.domino.helpers.DatabaseMetaData;
import org.openntf.domino.helpers.DbDirectoryTree;
import org.openntf.domino.types.Encapsulated;
import org.openntf.domino.utils.DominoUtils;
// TODO: Auto-generated Javadoc
/**
* The Class DbDirectory.
*/
public class DbDirectory extends BaseNonThreadSafe<org.openntf.domino.DbDirectory, lotus.domino.DbDirectory, Session> implements
org.openntf.domino.DbDirectory, Encapsulated {
private static final Logger log_ = Logger.getLogger(DbDirectory.class.getName());
/* the MetaData contains s small subset of information of a (closed) Database */
private transient SortedSet<DatabaseMetaData> dbMetaDataSet_;
private transient Iterator<DatabaseMetaData> dbIter; // required for getFirst/getNextDatabase
private transient DbDirectoryTree dbDirectoryTree_;
private org.openntf.domino.DbDirectory.Type type_ = Type.TEMPLATE_CANDIDATE;
private String name_;
private String clusterName_;
private boolean isHonorOpenDialog_ = false;
private Comparator<DatabaseMetaData> comparator_ = DatabaseMetaData.FILEPATH_COMPARATOR;
/**
* Instantiates a new DbDirectory.
*
* @param delegate
* the delegate
* @param parent
* the parent
* @param wf
* the wrapperfactory
* @param cppId
* the cpp-id
*/
protected DbDirectory(final lotus.domino.DbDirectory delegate, final Session parent) {
super(delegate, parent, NOTES_SERVER);
try {
name_ = delegate.getName();
clusterName_ = delegate.getClusterName();
} catch (NotesException e) {
DominoUtils.handleException(e);
}
//dbHolderSet_ = new ConcurrentSkipListSet<DatabaseMetaData>(DatabaseMetaData.FILEPATH_COMPARATOR);
}
@Override
@Deprecated
public boolean isSortByLastModified() {
return comparator_ == DatabaseMetaData.LASTMOD_COMPARATOR;
}
@Override
@Deprecated
public void setSortByLastModified(final boolean value) {
if (value) {
setComparator(DatabaseMetaData.LASTMOD_COMPARATOR);
} else {
setComparator(DatabaseMetaData.FILEPATH_COMPARATOR);
}
}
protected void setComparator(final Comparator<DatabaseMetaData> cmp) {
if (comparator_ != cmp) {
comparator_ = cmp;
reset();
}
}
@Override
public Type getDirectoryType() {
return type_;
}
@Override
public void setDirectoryType(final Type type) {
type_ = type;
if (type_ != type) {
reset();
}
}
/**
* Convert the lotus int-Type to org.openntf.domino.DbDirectory.Type
*
* @param iType
* @return
*/
protected Type convertType(final int iType) {
for (Type t : Type.values()) {
if (t.getValue() == iType)
return t;
}
throw new IllegalArgumentException("The type " + iType + " is not a valid DbDirectory type");
}
private void reset() {
dbMetaDataSet_ = null;
}
private void initialize(final lotus.domino.DbDirectory delegate) {
dbMetaDataSet_ = new ConcurrentSkipListSet<DatabaseMetaData>(comparator_);
// boolean isExtended = type_ == Type.REPLICA_CANDIDATE || isDateSorted_;
boolean isExtended = isSortByLastModified(); // || type_ == Type.REPLICA_CANDIDATE; CHECKME: Is there really an issue with REPLICA_CANDIDATE
try {
lotus.domino.Database rawdb = null;
try {
int type = type_.getValue();
rawdb = delegate.getFirstDatabase(type);
} catch (NotesException ne) {
log_.log(Level.WARNING, "For some reason getting the first database reported an exception: " + ne.text
+ " Attempting to move along...");
rawdb = delegate.getNextDatabase();
}
while (rawdb != null) {
boolean wasOpen = rawdb.isOpen(); // remember, if the database was open or not!
// try to open the DB for extended infos
if (isExtended) {
try {
rawdb.open();
} catch (NotesException tmp) {
log_.log(Level.FINE,
"Unable to read extended infos from database: " + rawdb.getServer() + "!!" + rawdb.getFilePath());
}
}
dbMetaDataSet_.add(new DatabaseMetaData(rawdb));
if (wasOpen) {
getFactory().fromLotus(rawdb, Database.SCHEMA, getAncestorSession());
} else {
Base.s_recycle(rawdb);
}
rawdb = delegate.getNextDatabase();
}
Base.s_recycle(delegate);
} catch (NotesException ne) {
ne.printStackTrace();
}
// try {
//
// delegate.setHonorShowInOpenDatabaseDialog(isHonorOpenDialog_);
//
//
// // Trying to iterate multiple times over the DbDirectory
// for (int i = 0; i < 16; i++) {
// int cnt = 0;
// rawdb = delegate.getFirstDatabase(1245 + (i % 4));
// while (rawdb != null) {
// cnt++;
// try {
// rawdb.open();
// } catch (NotesException ne) {
// }
// rawdb.recycle();
// rawdb = delegate.getNextDatabase();
// }
// System.out.println("iteration " + i + " DBs: " + cnt);
// }
//
// try {
// rawdb = delegate.getFirstDatabase(type_.getValue());
// } catch (NotesException ne) {
// log_.log(Level.WARNING, "For some reason getting the first database reported an exception: " + ne.text
// + " Attempting to move along...");
// rawdb = delegate.getNextDatabase();
// }
// lotus.domino.Database nextdb;
// Database db;
// boolean accessible = false;
// lotus.domino.Database firstDb = rawdb;
// while (rawdb != null) {
// nextdb = delegate.getNextDatabase();
//
// // RPr: the dbDirectory really sucks...
// // Problem 1: Normally the dbDirectory provides "closed" databases
// // this means, you get also database objects you do not have access to. If we try to get such a database later by apiPath from the session
// // it will fail!
// //
// // Problem 2: Sometimes you will get the same database objects that were opened somwhere else in your code. This means if you recycle
// // the db while you are iterating, this will invalidate the db that was somewhere else opened!
//
// // TODO RPr: Should we do that with factory
// // YES, we MUST do this in the factory, otherwise we will get errors like: PANIC! Why are we recaching a lotus object" because there are
// // two openntf.domino instances for the same cpp id
// db = getFactory().fromLotus(rawdb, Database.SCHEMA, null);
//
// // And we MUST NOT use this constructor, as it recycles the delegate (which means that we close databases, that we have opened somewhere else in our code)
// //db = new org.openntf.domino.impl.Database(rawdb, this, isExtended);
//
// if (type_ == Type.REPLICA_CANDIDATE) {
// if (org.openntf.domino.Database.Utils.isReplicaCandidate(db))
// dbHolderSet_.add(new DatabaseMetaData(db));
// } else if (type_ == Type.TEMPLATE) {
// if (org.openntf.domino.Database.Utils.isTemplate(db))
// dbHolderSet_.add(new DatabaseMetaData(db));
// } else if (type_ == Type.DATABASE) {
// if (org.openntf.domino.Database.Utils.isDatabase(db))
// dbHolderSet_.add(new DatabaseMetaData(db));
// } else if (type_ == Type.XOTS_DATABASE) {
// if (org.openntf.domino.Database.Utils.isXotsDatabase(db))
// dbHolderSet_.add(new DatabaseMetaData(db));
// } else {
// if (org.openntf.domino.Database.Utils.isTemplateCandidate(db))
// dbHolderSet_.add(new DatabaseMetaData(db));
// }
// // the "db" object will get out of scope here, so that it can get recycled through the GC
// rawdb = nextdb;
// }
// Base.s_recycle(delegate);
// } catch (NotesException ne) {
// ne.printStackTrace();
// }
// isInitialized_ = true;
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.DbDirectory#createDatabase(java.lang.String)
*/
@Override
public Database createDatabase(final String dbFile) {
try {
return fromLotus(getDelegate().createDatabase(dbFile), Database.SCHEMA, getAncestorSession());
} catch (NotesException e) {
DominoUtils.handleException(e);
return null;
}
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.DbDirectory#createDatabase(java.lang.String, boolean)
*/
@Override
public Database createDatabase(final String dbFile, final boolean open) {
org.openntf.domino.Database result = null;
try {
if (getAncestorSession().isFixEnabled(Fixes.CREATE_DB)) {
result = openDatabase(dbFile, false);
if (result != null)
return result;
}
lotus.domino.Database delDb = getDelegate().createDatabase(dbFile, open);
result = fromLotus(delDb, Database.SCHEMA, getAncestorSession());
return result;
} catch (NotesException e) {
// System.out.println("DEBUG: " + e.id + " - " + e.text);
if (e.id == 4005 && getAncestorSession().isFixEnabled(Fixes.CREATE_DB)) {
result = getAncestorSession().getDatabase(getName(), dbFile);
return result;
} else {
DominoUtils.handleException(e);
return null;
}
}
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.DbDirectory#getClusterName()
*/
@Override
public String getClusterName() {
return clusterName_;
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.DbDirectory#getClusterName(java.lang.String)
*/
@Override
public String getClusterName(final String server) {
try {
return getDelegate().getClusterName(server);
} catch (NotesException e) {
DominoUtils.handleException(e);
return null;
}
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.DbDirectory#getFirstDatabase(int)
*/
@Override
@Deprecated
@Legacy(org.openntf.domino.annotations.Legacy.ITERATION_WARNING)
public Database getFirstDatabase(final int type) {
setDirectoryType(convertType(type));
return getFirstDatabase();
}
/* (non-Javadoc)
* @see org.openntf.domino.DbDirectory#getFirstDatabase(org.openntf.domino.DbDirectory.Type)
*/
@Override
@Deprecated
@Legacy(org.openntf.domino.annotations.Legacy.ITERATION_WARNING)
public Database getFirstDatabase(final Type type) {
setDirectoryType(type);
return getFirstDatabase();
}
@Deprecated
@Legacy(org.openntf.domino.annotations.Legacy.ITERATION_WARNING)
public Database getFirstDatabase() {
dbIter = getMetaDataSet().iterator();
return getNextDatabase();
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.DbDirectory#getName()
*/
@Override
public String getName() {
return name_;
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.DbDirectory#getNextDatabase()
*/
@Override
@Deprecated
@Legacy(org.openntf.domino.annotations.Legacy.ITERATION_WARNING)
public Database getNextDatabase() {
// RPr: hopefully this will work the same way as the original lotus implementation does
if (dbIter.hasNext()) {
return getFactory().create(Database.SCHEMA, getAncestorSession(), dbIter.next());
}
return null;
// This will never work, as the DBs in the getDbHolderSet() are in a complete different order
// try {
// return fromLotus(getDelegate().getNextDatabase(), Database.SCHEMA, getAncestorSession());
// } catch (NotesException e) {
// DominoUtils.handleException(e);
// return null;
// }
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.impl.Base#getParent()
*/
@Override
public final Session getParent() {
return parent;
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.DbDirectory#isHonorShowInOpenDatabaseDialog()
*/
@Override
public boolean isHonorShowInOpenDatabaseDialog() {
return isHonorOpenDialog_;
}
private SortedSet<DatabaseMetaData> getMetaDataSet() {
// if (!isInitialized_) {
// initialize(getDelegate());
// }
// return dbHolderSet_;
if (dbMetaDataSet_ == null) {
initialize(getDelegate());
}
return dbMetaDataSet_;
}
/*
* (non-Javadoc)
*
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<org.openntf.domino.Database> iterator() {
// Create a proxy iterator
return new Iterator<org.openntf.domino.Database>() {
final Iterator<DatabaseMetaData> metaIter_ = getMetaDataSet().iterator();
@Override
public boolean hasNext() {
return metaIter_.hasNext();
}
@Override
public Database next() {
return getFactory().create(Database.SCHEMA, getAncestorSession(), metaIter_.next());
}
@Override
public void remove() {
metaIter_.remove();
}
};
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.DbDirectory#openDatabase(java.lang.String)
*/
@Override
public Database openDatabase(final String dbFile) {
return getAncestorSession().getDatabase(getName(), dbFile);
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.DbDirectory#openDatabase(java.lang.String, boolean)
*/
@Override
public Database openDatabase(final String dbFile, final boolean failover) {
if (failover) {
return getAncestorSession().getDatabaseWithFailover(getName(), dbFile);
} else {
return openDatabase(dbFile);
}
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.DbDirectory#openDatabaseByReplicaID(java.lang.String)
*/
@Override
public Database openDatabaseByReplicaID(final String replicaId) {
return getAncestorSession().getDatabase(getName(), replicaId);
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.DbDirectory#openDatabaseIfModified(java.lang.String, lotus.domino.DateTime)
*/
@Override
public Database openDatabaseIfModified(final String dbFile, final lotus.domino.DateTime date) {
return getAncestorSession().getDatabaseIfModified(getName(), dbFile, date);
}
@Override
public Database openDatabaseIfModified(final String dbFile, final Date date) {
return getAncestorSession().getDatabaseIfModified(getName(), dbFile, date);
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.DbDirectory#openMailDatabase()
*/
@Override
public Database openMailDatabase() {
return getAncestorSession().getMailDatabase();
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.DbDirectory#setHonorShowInOpenDatabaseDialog(boolean)
*/
@Override
public void setHonorShowInOpenDatabaseDialog(final boolean flag) {
if (isHonorOpenDialog_ != flag) {
isHonorOpenDialog_ = flag;
reset();
}
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.types.SessionDescendant#getAncestorSession()
*/
@Override
public final Session getAncestorSession() {
return parent;
}
@Override
protected void resurrect() {
lotus.domino.Session rawSession = toLotus(parent);
try {
lotus.domino.DbDirectory dir = rawSession.getDbDirectory(name_);
dir.setHonorShowInOpenDatabaseDialog(isHonorOpenDialog_);
if (log_.isLoggable(java.util.logging.Level.FINE)) {
Throwable t = new Throwable();
log_.log(java.util.logging.Level.FINE, "DbDirectory for server " + name_ + "had been recycled and was auto-restored.", t);
}
setDelegate(dir, true);
} catch (Exception e) {
DominoUtils.handleException(e);
}
}
/* (non-Javadoc)
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
*/
@Override
@SuppressWarnings("unchecked")
public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
comparator_ = (Comparator<DatabaseMetaData>) in.readObject();
isHonorOpenDialog_ = in.readBoolean();
type_ = Type.valueOf(in.readInt());
name_ = in.readUTF();
clusterName_ = in.readUTF();
// CHECKME should we really
dbMetaDataSet_ = (SortedSet<DatabaseMetaData>) in.readObject();
}
/* (non-Javadoc)
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
*/
@Override
public void writeExternal(final ObjectOutput out) throws IOException {
out.writeObject(comparator_);
out.writeBoolean(isHonorOpenDialog_);
out.writeInt(type_.getValue());
out.writeUTF(name_);
out.writeUTF(clusterName_);
out.writeObject(dbMetaDataSet_);
}
@Override
public boolean add(final Database db) {
try {
return getMetaDataSet().add(new DatabaseMetaData(db));
} catch (NotesException ne) {
DominoUtils.handleException(ne);
return false;
}
}
@Override
public boolean addAll(final Collection<? extends Database> dbs) {
boolean ret = false;
for (Database db : dbs) {
if (add(db)) {
ret = true;
}
}
return ret;
}
@Override
public void clear() {
if (dbMetaDataSet_ == null) {
dbMetaDataSet_ = new ConcurrentSkipListSet<DatabaseMetaData>(comparator_);
} else {
dbMetaDataSet_.clear();
}
}
@Override
public boolean contains(final Object obj) {
if (obj instanceof Database) {
try {
return getMetaDataSet().contains(new DatabaseMetaData((Database) obj));
} catch (NotesException ne) {
DominoUtils.handleException(ne);
return false;
}
} else {
return false;
}
}
@Override
public boolean containsAll(final Collection<?> objs) {
for (Object obj : objs) {
if (!contains(obj)) {
return false;
}
}
return true;
}
@Override
public boolean isEmpty() {
return getMetaDataSet().isEmpty();
}
@Override
public boolean remove(final Object obj) {
if (obj instanceof Database) {
try {
return getMetaDataSet().remove(new DatabaseMetaData((Database) obj));
} catch (NotesException ne) {
DominoUtils.handleException(ne);
return false;
}
} else {
return false;
}
}
@Override
public boolean removeAll(final Collection<?> objs) {
boolean ret = false;
for (Object obj : objs) {
if (remove(obj)) {
ret = true;
}
}
return ret;
}
@Override
public boolean retainAll(final Collection<?> objs) {
Collection<DatabaseMetaData> holders = new ArrayList<DatabaseMetaData>(objs.size());
for (Object obj : objs) {
if (obj instanceof Database) {
try {
holders.add(new DatabaseMetaData((Database) obj));
} catch (NotesException ne) {
DominoUtils.handleException(ne);
return false;
}
}
}
return getMetaDataSet().retainAll(holders);
}
@Override
public int size() {
return getMetaDataSet().size();
}
@Override
public Object[] toArray() {
Object[] ret = new Object[size()];
int i = 0;
for (DatabaseMetaData metaData : getMetaDataSet()) {
Database db = getFactory().create(Database.SCHEMA, getAncestorSession(), metaData);
ret[i++] = db;
}
return ret;
}
@SuppressWarnings("unchecked")
@Override
public <T> T[] toArray(T[] paramArrayOfT) {
// DUH!
if (size() > paramArrayOfT.length) {
Class<?> localClass = paramArrayOfT.getClass().getComponentType();
paramArrayOfT = (T[]) Array.newInstance(localClass, size());
}
System.arraycopy(toArray(), 0, paramArrayOfT, 0, size());
if (size() < paramArrayOfT.length) {
paramArrayOfT[size()] = null;
}
return paramArrayOfT;
}
@Override
public DbDirectoryTree getTree(final Type type) {
setDirectoryType(type);
return getTree();
}
@Override
public DbDirectoryTree getTree() {
if (dbDirectoryTree_ == null)
dbDirectoryTree_ = new DbDirectoryTree(getMetaDataSet(), getAncestorSession());
return dbDirectoryTree_;
}
@Override
protected WrapperFactory getFactory() {
return parent.getFactory();
}
}