diff options
author | alee <alee@c9f7a03b-bd48-0410-a16d-cbbf54688b0b> | 2009-01-05 16:58:25 +0000 |
---|---|---|
committer | alee <alee@c9f7a03b-bd48-0410-a16d-cbbf54688b0b> | 2009-01-05 16:58:25 +0000 |
commit | 12037cf9cf935bda5259e531b11a876ff2b41098 (patch) | |
tree | 2dd9029e94369398638d8467ddd895acae80cfb3 /pki/base/common/src/com/netscape/cmscore/dbs | |
parent | dca02910f6de3f9631589ee3c12c229fa58629c8 (diff) | |
download | pki-12037cf9cf935bda5259e531b11a876ff2b41098.tar.gz pki-12037cf9cf935bda5259e531b11a876ff2b41098.tar.xz pki-12037cf9cf935bda5259e531b11a876ff2b41098.zip |
BZ472006 Serial number management
git-svn-id: svn+ssh://svn.fedorahosted.org/svn/pki/trunk@168 c9f7a03b-bd48-0410-a16d-cbbf54688b0b
Diffstat (limited to 'pki/base/common/src/com/netscape/cmscore/dbs')
5 files changed, 817 insertions, 90 deletions
diff --git a/pki/base/common/src/com/netscape/cmscore/dbs/CertificateRepository.java b/pki/base/common/src/com/netscape/cmscore/dbs/CertificateRepository.java index 4d84b1fdf..43a520bd5 100644 --- a/pki/base/common/src/com/netscape/cmscore/dbs/CertificateRepository.java +++ b/pki/base/common/src/com/netscape/cmscore/dbs/CertificateRepository.java @@ -213,7 +213,7 @@ public class CertificateRepository extends Repository public CertStatusUpdateThread mCertStatusUpdateThread = null; public RetrieveModificationsThread mRetrieveModificationsThread = null; - public void setCertStatusUpdateInterval(int interval, boolean listenToCloneModifications) { + public void setCertStatusUpdateInterval(IRepository requestRepo, int interval, boolean listenToCloneModifications) { CMS.debug("In setCertStatusUpdateInterval " + interval); if (interval == 0) { CMS.debug("In setCertStatusUpdateInterval interval = 0" + interval); @@ -246,7 +246,7 @@ public class CertificateRepository extends Repository CMS.debug("In setCertStatusUpdateInterval mCertStatusUpdateThread " + mCertStatusUpdateThread); if (mCertStatusUpdateThread == null) { CMS.debug("In setCertStatusUpdateInterval about to create CertStatusUpdateThread "); - mCertStatusUpdateThread = new CertStatusUpdateThread(this, "CertStatusUpdateThread"); + mCertStatusUpdateThread = new CertStatusUpdateThread(this, requestRepo, "CertStatusUpdateThread"); mCertStatusUpdateThread.setInterval(interval); mCertStatusUpdateThread.start(); } else { @@ -1893,14 +1893,16 @@ public class CertificateRepository extends Repository class CertStatusUpdateThread extends Thread { CertificateRepository _cr = null; + IRepository _rr = null; int _interval; - CertStatusUpdateThread(CertificateRepository cr, String name) { + CertStatusUpdateThread(CertificateRepository cr, IRepository rr, String name) { super(name); CMS.debug("new CertStatusUpdateThread"); //setName(name); _cr = cr; + _rr = rr; } public void setInterval(int interval) { @@ -1919,10 +1921,18 @@ class CertStatusUpdateThread extends Thread { CMS.debug("Starting updateCertStatus (entered lock)"); _cr.updateCertStatus(); CMS.debug("updateCertStatus done"); + + CMS.debug("Starting cert checkRanges"); + _cr.checkRanges(); + CMS.debug("cert checkRanges done"); + + CMS.debug("Starting request checkRanges"); + _rr.checkRanges(); + CMS.debug("request checkRanges done"); } } catch (Exception e) { - CMS.debug("updateCertStatus done"); + CMS.debug("updateCertStatus done: " + e.toString()); } try { sleep(_interval * 1000); diff --git a/pki/base/common/src/com/netscape/cmscore/dbs/DBSubsystem.java b/pki/base/common/src/com/netscape/cmscore/dbs/DBSubsystem.java index 30c708446..3038e0026 100644 --- a/pki/base/common/src/com/netscape/cmscore/dbs/DBSubsystem.java +++ b/pki/base/common/src/com/netscape/cmscore/dbs/DBSubsystem.java @@ -59,12 +59,12 @@ public class DBSubsystem implements IDBSubsystem { private DBRegistry mRegistry = null; private String mBaseDN = null; private ISubsystem mOwner = null; + + private Hashtable[] mRepos = null; + private BigInteger mNextSerialConfig = null; - private String mMaxSerialConfig = null; - private String mMinSerialConfig = null; + private boolean mEnableSerialMgmt = false; - private String mMinRequestConfig=null; - private String mMaxRequestConfig=null; private static final String PEOPLE_DN = "ou=people"; private static final String GROUPS_DN = "ou=groups"; private static final String REQUESTS_DN = "ou=requests"; @@ -81,6 +81,7 @@ public class DBSubsystem implements IDBSubsystem { private static final String KRA_DN = "ou=kra"; private static final String KR_DN = "ou=keyRepository, ou=kra"; private static final String KRA_REQUESTS_DN = "ou=kra, ou=requests"; + private static final String REPLICA_DN = "ou=replica"; private static final String PROP_ENABLE_SERIAL_NUMBER_RECOVERY = "enableSerialNumberRecovery"; // This value is only equal to the next Serial number that the CA's @@ -90,17 +91,58 @@ public class DBSubsystem implements IDBSubsystem { private static final String PROP_NEXT_SERIAL_NUMBER = "nextSerialNumber"; private static final String PROP_MIN_SERIAL_NUMBER="beginSerialNumber"; - - private static final String PROP_MAX_SERIAL_NUMBER = - "endSerialNumber"; + private static final String PROP_MAX_SERIAL_NUMBER = "endSerialNumber"; + private static final String PROP_NEXT_MIN_SERIAL_NUMBER="nextBeginSerialNumber"; + private static final String PROP_NEXT_MAX_SERIAL_NUMBER ="nextEndSerialNumber"; + private static final String PROP_SERIAL_LOW_WATER_MARK="serialLowWaterMark"; + private static final String PROP_SERIAL_INCREMENT="serialIncrement"; + private static final String PROP_SERIAL_BASEDN="serialDN"; + private static final String PROP_SERIAL_RANGE_DN="serialRangeDN"; private static final String PROP_MIN_REQUEST_NUMBER="beginRequestNumber"; private static final String PROP_MAX_REQUEST_NUMBER="endRequestNumber"; + private static final String PROP_NEXT_MIN_REQUEST_NUMBER="nextBeginRequestNumber"; + private static final String PROP_NEXT_MAX_REQUEST_NUMBER="nextEndRequestNumber"; + private static final String PROP_REQUEST_LOW_WATER_MARK="requestLowWaterMark"; + private static final String PROP_REQUEST_INCREMENT="requestIncrement"; + private static final String PROP_REQUEST_BASEDN="requestDN"; + private static final String PROP_REQUEST_RANGE_DN="requestRangeDN"; + + private static final String PROP_MIN_REPLICA_NUMBER="beginReplicaNumber"; + private static final String PROP_MAX_REPLICA_NUMBER = "endReplicaNumber"; + private static final String PROP_NEXT_MIN_REPLICA_NUMBER="nextBeginReplicaNumber"; + private static final String PROP_NEXT_MAX_REPLICA_NUMBER ="nextEndReplicaNumber"; + private static final String PROP_REPLICA_LOW_WATER_MARK="replicaLowWaterMark"; + private static final String PROP_REPLICA_INCREMENT="replicaIncrement"; + private static final String PROP_REPLICA_BASEDN="replicaDN"; + private static final String PROP_REPLICA_RANGE_DN="replicaRangeDN"; private static final String PROP_INFINITE_SERIAL_NUMBER = "1000000000"; private static final String PROP_INFINITE_REQUEST_NUMBER = "1000000000"; + private static final String PROP_INFINITE_REPLICA_NUMBER = "1000"; private static final String PROP_BASEDN = "basedn"; private static final String PROP_LDAP = "ldap"; + private static final String PROP_NEXT_RANGE = "nextRange"; + private static final String PROP_ENABLE_SERIAL_MGMT = "enableSerialManagement"; + + // hash keys + private static final String NAME="name"; + private static final String PROP_MIN="min"; + private static final String PROP_MIN_NAME="min_name"; + private static final String PROP_MAX = "max"; + private static final String PROP_MAX_NAME = "max_name"; + private static final String PROP_NEXT_MIN="next_min"; + private static final String PROP_NEXT_MIN_NAME="next_min_name"; + private static final String PROP_NEXT_MAX = "next_max"; + private static final String PROP_NEXT_MAX_NAME = "next_max_name"; + private static final String PROP_LOW_WATER_MARK="lowWaterMark"; + private static final String PROP_LOW_WATER_MARK_NAME="lowWaterMark_name"; + private static final String PROP_INCREMENT = "increment"; + private static final String PROP_INCREMENT_NAME = "increment_name"; + private static final String PROP_RANGE_DN="rangeDN"; + + private static final BigInteger BI_ONE = new BigInteger("1"); + private ILogger mLogger = null; // singleton enforcement @@ -152,6 +194,24 @@ public class DBSubsystem implements IDBSubsystem { } } + public boolean getEnableSerialMgmt() { + return mEnableSerialMgmt; + } + + public void setEnableSerialMgmt(boolean v) + throws EBaseException { + if (v) { + CMS.debug("DBSubsystem: Enabling Serial Number Management"); + } else { + CMS.debug("DBSubsystem: Disabling Serial Number Management"); + } + + mDBConfig.putBoolean(PROP_ENABLE_SERIAL_MGMT, v); + IConfigStore rootStore = getOwner().getConfigStore(); + rootStore.commit(false); + mEnableSerialMgmt = v; + } + public BigInteger getNextSerialConfig() { return mNextSerialConfig; } @@ -165,31 +225,297 @@ public class DBSubsystem implements IDBSubsystem { serial.toString(16)); } - public String getMinSerialConfig() + /** + * Gets minimum serial number limit in config file + * + * @param repo repo identifier + * @return min serial number + */ + public String getMinSerialConfig(int repo) { - return mMinSerialConfig; + return (String) (mRepos[repo]).get(PROP_MIN); } - public String getMaxSerialConfig() { - return mMaxSerialConfig; + + /** + * Gets maximum serial number limit in config file + * + * @param repo repo identifier + * @return max serial number + */ + public String getMaxSerialConfig(int repo) { + return (String) (mRepos[repo]).get(PROP_MAX); } - public String getMinRequestConfig() + /** + * Gets minimum serial number limit in next range in config file + * + * @param repo repo identifier + * @return min serial number in next range + */ + public String getNextMinSerialConfig(int repo) { - return mMinRequestConfig; + String ret = (String) (mRepos[repo]).get(PROP_NEXT_MIN); + if (ret.equals("-1")) { + return null; + } + else { + return ret; + } + } + + /** + * Gets maximum serial number limit in next range in config file + * + * @param repo repo identifier + * @return max serial number in next range + */ + public String getNextMaxSerialConfig(int repo) { + String ret = (String) (mRepos[repo]).get(PROP_NEXT_MAX); + if (ret.equals("-1")) { + return null; + } + else { + return ret; + } + } + + /** + * Gets low water mark limit in config file + * + * @param repo repo identifier + * @return low water mark + */ + public String getLowWaterMarkConfig(int repo) { + return (String) (mRepos[repo]).get(PROP_LOW_WATER_MARK); } - public String getMaxRequestConfig() + /** + * Gets range increment for next range in config file + * + * @param repo repo identifier + * @return range increment + */ + public String getIncrementConfig(int repo) { - return mMaxRequestConfig; + return (String) (mRepos[repo]).get(PROP_INCREMENT); } - public void setMaxSerialConfig(String serial) + /** + * Sets maximum serial number limit in config file + * + * @param repo repo identifier + * @param serial max serial number + * @exception EBaseException failed to set + */ + public void setMaxSerialConfig(int repo, String serial) throws EBaseException { - mLogger.log(ILogger.EV_SYSTEM, ILogger.S_DB, - ILogger.LL_INFO, "DBSubsystem: " + - "Setting max serial number: 0x" + serial); - mDBConfig.putString(PROP_MAX_SERIAL_NUMBER, - serial); + Hashtable h = mRepos[repo]; + CMS.debug("DBSubsystem: Setting max serial number for " + h.get(NAME) + ": " + serial); + + //persist to file + mDBConfig.putString((String) h.get(PROP_MAX_NAME), serial); + IConfigStore rootStore = getOwner().getConfigStore(); + rootStore.commit(false); + + h.put(PROP_MAX, serial); + mRepos[repo] = h; + } + + /** + * Sets minimum serial number limit in config file + * + * @param repo repo identifier + * @param serial min serial number + * @exception EBaseException failed to set + */ + public void setMinSerialConfig(int repo, String serial) + throws EBaseException { + Hashtable h = mRepos[repo]; + CMS.debug("DBSubsystem: Setting min serial number for " + h.get(NAME) + ": " + serial); + + //persist to file + mDBConfig.putString((String) h.get(PROP_MIN_NAME), serial); + IConfigStore rootStore = getOwner().getConfigStore(); + rootStore.commit(false); + + h.put(PROP_MIN, serial); + mRepos[repo] = h; + } + + /** + * Sets maximum serial number limit for next range in config file + * + * @param repo repo identifier + * @param serial max serial number for next range + * @exception EBaseException failed to set + */ + public void setNextMaxSerialConfig(int repo, String serial) + throws EBaseException { + Hashtable h = mRepos[repo]; + if (serial == null) { + CMS.debug("DBSubsystem: Removing next max " + h.get(NAME) + " number"); + mDBConfig.remove((String) h.get(PROP_NEXT_MAX_NAME)); + } else { + CMS.debug("DBSubsystem: Setting next max " + h.get(NAME) + " number: " + serial); + mDBConfig.putString((String) h.get(PROP_NEXT_MAX_NAME), serial); + } + IConfigStore rootStore = getOwner().getConfigStore(); + rootStore.commit(false); + if (serial == null) { + Object o2 = h.remove(PROP_NEXT_MAX); + } else { + h.put(PROP_NEXT_MAX, serial); + } + mRepos[repo] = h; + } + + /** + * Sets minimum serial number limit for next range in config file + * + * @param repo repo identifier + * @param serial min serial number for next range + * @exception EBaseException failed to set + */ + public void setNextMinSerialConfig(int repo, String serial) + throws EBaseException { + Hashtable h = mRepos[repo]; + if (serial == null) { + CMS.debug("DBSubsystem: Removing next min " + h.get(NAME) + " number"); + mDBConfig.remove((String) h.get(PROP_NEXT_MIN_NAME)); + } else { + CMS.debug("DBSubsystem: Setting next min " + h.get(NAME) + " number: " + serial); + mDBConfig.putString((String) h.get(PROP_NEXT_MIN_NAME), serial); + } + IConfigStore rootStore = getOwner().getConfigStore(); + rootStore.commit(false); + if (serial == null) { + Object o2 = h.remove(PROP_NEXT_MIN); + } else { + h.put(PROP_NEXT_MIN, serial); + } + mRepos[repo] = h; + } + + /** + * Gets start of next range from database. + * Increments the nextRange attribute and allocates + * this range to the current instance by creating a pkiRange object. + * + * @param repo repo identifier + * @return start of next range + */ + public String getNextRange(int repo) { + LDAPConnection conn = null; + String nextRange = null; + try { + Hashtable h = mRepos[repo]; + conn = mLdapConnFactory.getConn(); + String dn = (String) h.get(PROP_BASEDN) + "," + mBaseDN; + String rangeDN = (String) h.get(PROP_RANGE_DN) + "," + mBaseDN; + + LDAPEntry entry = conn.read(dn); + LDAPAttribute attr = entry.getAttribute(PROP_NEXT_RANGE); + nextRange = (String) attr.getStringValues().nextElement(); + + BigInteger nextRangeNo = new BigInteger(nextRange); + if (nextRangeNo == null) { + throw new EBaseException("nextRangeNo is null!"); + } + + BigInteger incrementNo = new BigInteger((String) h.get(PROP_INCREMENT)); + if (incrementNo == null) { + throw new EBaseException("incrementNo is null!"); + } + + // To make sure attrNextRange always increments, first delete the current value and then + // increment. Two operations in the same transaction + + LDAPAttribute attrNextRange = new LDAPAttribute(PROP_NEXT_RANGE, nextRangeNo.add(incrementNo).toString()); + LDAPModification [] mods = { + new LDAPModification( LDAPModification.DELETE, attr), + new LDAPModification( LDAPModification.ADD, attrNextRange ) }; + conn.modify( dn, mods ); + + // Add new range object + String endRange = nextRangeNo.add(incrementNo).subtract(BI_ONE).toString(); + LDAPAttributeSet attrs = new LDAPAttributeSet(); + attrs.add(new LDAPAttribute("objectClass", "top")); + attrs.add(new LDAPAttribute("objectClass", "pkiRange")); + attrs.add(new LDAPAttribute("beginRange" , nextRange)); + attrs.add(new LDAPAttribute("endRange" , endRange)); + attrs.add(new LDAPAttribute("cn", nextRange)); + attrs.add(new LDAPAttribute("host", CMS.getEESSLHost())); + attrs.add(new LDAPAttribute("securePort", CMS.getEESSLPort())); + String dn2 = "cn=" + nextRange + "," + rangeDN; + LDAPEntry rangeEntry = new LDAPEntry(dn2, attrs); + conn.add(rangeEntry); + } catch (Exception e) { + CMS.debug("DBSubsystem: getNextRange. Unable to provide next range :" + e); + e.printStackTrace(); + nextRange = null; + } finally { + try { + if ((conn != null) && (mLdapConnFactory!= null)) { + CMS.debug("Releasing ldap connection"); + mLdapConnFactory.returnConn(conn); + } + } + catch (Exception e) { + CMS.debug("Error releasing the ldap connection" + e.toString()); + } + } + return nextRange; + } + + /** + * Determines if a range conflict has been observed in database. + * If so, delete the conflict entry and remove the next range. + * When the next number is requested, if the number of certs is still + * below the low water mark, then a new range will be requested. + * + * @param repo repo identifier + * @return true if range conflict, false otherwise + */ + public boolean hasRangeConflict(int repo) + { + LDAPConnection conn = null; + boolean conflict = false; + try { + String nextRangeStart = getNextMinSerialConfig(repo); + if (nextRangeStart == null) { + return false; + } + Hashtable h = mRepos[repo]; + conn = mLdapConnFactory.getConn(); + String rangedn = (String) h.get(PROP_RANGE_DN) + "," + mBaseDN; + String filter = "(&(nsds5ReplConflict=*)(objectClass=pkiRange)(host= " + + CMS.getEESSLHost() + ")(SecurePort=" + CMS.getEESSLPort() + + ")(beginRange=" + nextRangeStart + "))"; + LDAPSearchResults results = conn.search(rangedn, LDAPv3.SCOPE_SUB, + filter, null, false); + + while (results.hasMoreElements()) { + conflict = true; + LDAPEntry entry = results.next(); + String dn = entry.getDN(); + CMS.debug("Deleting conflict entry:" + dn); + conn.delete(dn); + } + } catch (Exception e) { + CMS.debug("DBSubsystem: hasRangeConflict. Error while checking next range." + e); + e.printStackTrace(); + } finally { + try { + if ((conn != null) && (mLdapConnFactory!= null)) { + CMS.debug("Releasing ldap connection"); + mLdapConnFactory.returnConn(conn); + } + } + catch (Exception e) { + CMS.debug("Error releasing the ldap connection" + e.toString()); + } + } + return conflict; } public ISubsystem getOwner() { @@ -205,8 +531,10 @@ public class DBSubsystem implements IDBSubsystem { public void init(ISubsystem owner, IConfigStore config) throws EBaseException { + mLogger = CMS.getLogger(); mDBConfig = config; + mRepos = new Hashtable[IDBSubsystem.NUM_REPOS]; mConfig = config.getSubStore(PROP_LDAP); IConfigStore tmpConfig = null; @@ -214,46 +542,109 @@ public class DBSubsystem implements IDBSubsystem { mBaseDN = mConfig.getString(PROP_BASEDN, "o=NetscapeCertificateServer"); mOwner = owner; + mNextSerialConfig = new BigInteger(mDBConfig.getString( - PROP_NEXT_SERIAL_NUMBER, "0"), 16); + PROP_NEXT_SERIAL_NUMBER, "0"), 16); - mMinSerialConfig = mDBConfig.getString( - PROP_MIN_SERIAL_NUMBER,null); + mEnableSerialMgmt = mDBConfig.getBoolean(PROP_ENABLE_SERIAL_MGMT, false); - if(mMinSerialConfig == null) - { - mMinSerialConfig = "0"; - } + // populate the certs hash entry + Hashtable certs = new Hashtable(); + certs.put(NAME, "certs"); + certs.put(PROP_BASEDN, mDBConfig.getString(PROP_SERIAL_BASEDN,"")); + certs.put(PROP_RANGE_DN, mDBConfig.getString(PROP_SERIAL_RANGE_DN, "")); + certs.put(PROP_MIN_NAME, PROP_MIN_SERIAL_NUMBER); + certs.put(PROP_MIN, mDBConfig.getString( + PROP_MIN_SERIAL_NUMBER, "0")); - mMaxSerialConfig = mDBConfig.getString( - PROP_MAX_SERIAL_NUMBER,null ); + certs.put(PROP_MAX_NAME, PROP_MAX_SERIAL_NUMBER); + certs.put(PROP_MAX, mDBConfig.getString( + PROP_MAX_SERIAL_NUMBER, PROP_INFINITE_SERIAL_NUMBER)); - if(mMaxSerialConfig == null) - { - mMaxSerialConfig = PROP_INFINITE_SERIAL_NUMBER; - } + certs.put(PROP_NEXT_MIN_NAME, PROP_NEXT_MIN_SERIAL_NUMBER); + certs.put(PROP_NEXT_MIN, mDBConfig.getString( + PROP_NEXT_MIN_SERIAL_NUMBER, "-1")); - CMS.debug("DBSubsystem: mMinSerialConfig: " + mMinSerialConfig + " mMaxSerialConfig: " + mMaxSerialConfig); + certs.put(PROP_NEXT_MAX_NAME, PROP_NEXT_MAX_SERIAL_NUMBER); + certs.put(PROP_NEXT_MAX, mDBConfig.getString( + PROP_NEXT_MAX_SERIAL_NUMBER, "-1")); - mMinRequestConfig = mDBConfig.getString(PROP_MIN_REQUEST_NUMBER,null); + certs.put(PROP_LOW_WATER_MARK_NAME, PROP_SERIAL_LOW_WATER_MARK); + certs.put(PROP_LOW_WATER_MARK, mDBConfig.getString( + PROP_SERIAL_LOW_WATER_MARK, "5000")); - if(mMinRequestConfig == null) - { - CMS.debug("DBSubsystem: missing mMinSerialConfig value!"); - mMinRequestConfig = "0"; - } + certs.put(PROP_INCREMENT_NAME, PROP_SERIAL_INCREMENT); + certs.put(PROP_INCREMENT, mDBConfig.getString( + PROP_SERIAL_INCREMENT, PROP_INFINITE_SERIAL_NUMBER)); - mMaxRequestConfig = mDBConfig.getString(PROP_MAX_REQUEST_NUMBER,null); + mRepos[CERTS]=certs; - if(mMaxRequestConfig == null) - { - CMS.debug("DBSubsystem: missing mMaxSerialConfig value!"); - mMaxRequestConfig = PROP_INFINITE_REQUEST_NUMBER; + // populate the requests hash entry + Hashtable requests = new Hashtable(); + requests.put(NAME, "requests"); + requests.put(PROP_BASEDN, mDBConfig.getString(PROP_REQUEST_BASEDN,"")); + requests.put(PROP_RANGE_DN, mDBConfig.getString(PROP_REQUEST_RANGE_DN, "")); + + requests.put(PROP_MIN_NAME, PROP_MIN_REQUEST_NUMBER); + requests.put(PROP_MIN, mDBConfig.getString( + PROP_MIN_REQUEST_NUMBER, "0")); + + requests.put(PROP_MAX_NAME, PROP_MAX_REQUEST_NUMBER); + requests.put(PROP_MAX, mDBConfig.getString( + PROP_MAX_REQUEST_NUMBER, PROP_INFINITE_REQUEST_NUMBER)); + + requests.put(PROP_NEXT_MIN_NAME, PROP_NEXT_MIN_REQUEST_NUMBER); + requests.put(PROP_NEXT_MIN, mDBConfig.getString( + PROP_NEXT_MIN_REQUEST_NUMBER, "-1")); + + requests.put(PROP_NEXT_MAX_NAME, PROP_NEXT_MAX_REQUEST_NUMBER); + requests.put(PROP_NEXT_MAX, mDBConfig.getString( + PROP_NEXT_MAX_REQUEST_NUMBER, "-1")); + + requests.put(PROP_LOW_WATER_MARK_NAME, PROP_REQUEST_LOW_WATER_MARK); + requests.put(PROP_LOW_WATER_MARK, mDBConfig.getString( + PROP_REQUEST_LOW_WATER_MARK, "5000")); + + requests.put(PROP_INCREMENT_NAME, PROP_REQUEST_INCREMENT); + requests.put(PROP_INCREMENT, mDBConfig.getString( + PROP_REQUEST_INCREMENT, PROP_INFINITE_REQUEST_NUMBER)); + + mRepos[REQUESTS] = requests; + + // populate replica ID hash entry + Hashtable replicaID = new Hashtable(); + replicaID.put(NAME, "requests"); + replicaID.put(PROP_BASEDN, mDBConfig.getString(PROP_REPLICA_BASEDN,"")); + replicaID.put(PROP_RANGE_DN, mDBConfig.getString(PROP_REPLICA_RANGE_DN, "")); + + replicaID.put(PROP_MIN_NAME, PROP_MIN_REPLICA_NUMBER); + replicaID.put(PROP_MIN, mDBConfig.getString( + PROP_MIN_REPLICA_NUMBER, "1")); + + replicaID.put(PROP_MAX_NAME, PROP_MAX_REPLICA_NUMBER); + replicaID.put(PROP_MAX, mDBConfig.getString( + PROP_MAX_REPLICA_NUMBER, PROP_INFINITE_REPLICA_NUMBER)); + + replicaID.put(PROP_NEXT_MIN_NAME, PROP_NEXT_MIN_REPLICA_NUMBER); + replicaID.put(PROP_NEXT_MIN, mDBConfig.getString( + PROP_NEXT_MIN_REPLICA_NUMBER, "-1")); + + replicaID.put(PROP_NEXT_MAX_NAME, PROP_NEXT_MAX_REPLICA_NUMBER); + replicaID.put(PROP_NEXT_MAX, mDBConfig.getString( + PROP_NEXT_MAX_REPLICA_NUMBER, "-1")); + + replicaID.put(PROP_LOW_WATER_MARK_NAME, PROP_REPLICA_LOW_WATER_MARK); + replicaID.put(PROP_LOW_WATER_MARK, mDBConfig.getString( + PROP_REPLICA_LOW_WATER_MARK, "10")); + + replicaID.put(PROP_INCREMENT_NAME, PROP_REPLICA_INCREMENT); + replicaID.put(PROP_INCREMENT, mDBConfig.getString( + PROP_REPLICA_INCREMENT, PROP_INFINITE_REPLICA_NUMBER)); + + mRepos[REPLICA_ID] = replicaID; - } - CMS.debug("DBSubsystem: mMinRequestConfig: " + mMinRequestConfig + " mMaxRequestConfig: " + mMaxRequestConfig); // initialize registry mRegistry = new DBRegistry(); mRegistry.init(this, null); diff --git a/pki/base/common/src/com/netscape/cmscore/dbs/KeyRepository.java b/pki/base/common/src/com/netscape/cmscore/dbs/KeyRepository.java index 099c5bdcd..a631a17a9 100644 --- a/pki/base/common/src/com/netscape/cmscore/dbs/KeyRepository.java +++ b/pki/base/common/src/com/netscape/cmscore/dbs/KeyRepository.java @@ -44,6 +44,7 @@ import com.netscape.cmscore.dbs.*; */ public class KeyRepository extends Repository implements IKeyRepository { + public KeyStatusUpdateThread mKeyStatusUpdateThread = null; protected IDBSubsystem mDBService = null; /** @@ -126,6 +127,30 @@ public class KeyRepository extends Repository implements IKeyRepository { } } + public void setKeyStatusUpdateInterval(IRepository requestRepo, int interval) { + CMS.debug("In setKeyStatusUpdateInterval " + interval); + // don't run the thread if serial management is disabled. + if ((interval == 0) || (!mDBService.getEnableSerialMgmt())) { + CMS.debug("In setKeyStatusUpdateInterval interval = 0" + interval); + if (mKeyStatusUpdateThread != null) { + mKeyStatusUpdateThread.stop(); + } + return; + } + + CMS.debug("In setKeyStatusUpdateInterval mKeyStatusUpdateThread " + mKeyStatusUpdateThread); + if (mKeyStatusUpdateThread == null) { + CMS.debug("In setKeyStatusUpdateInterval about to create KeyStatusUpdateThread "); + mKeyStatusUpdateThread = new KeyStatusUpdateThread(this, requestRepo, "KeyStatusUpdateThread"); + mKeyStatusUpdateThread.setInterval(interval); + mKeyStatusUpdateThread.start(); + } else { + CMS.debug("In setKeyStatusUpdateInterval it thinks the thread is up already "); + mKeyStatusUpdateThread.setInterval(interval); + // dont do anything if we have a thread running already + } + } + public IDBSubsystem getDBSubsystem() { return mDBService; } @@ -472,6 +497,57 @@ CMS.debug("filter= " + filter); return ret ; } + + public void shutdown() { + if (mKeyStatusUpdateThread != null) + mKeyStatusUpdateThread.destroy(); + } + +} + +class KeyStatusUpdateThread extends Thread { + KeyRepository _kr = null; + IRepository _rr = null; + int _interval; + + KeyStatusUpdateThread(KeyRepository kr, IRepository rr, String name) { + super(name); + CMS.debug("new KeyStatusUpdateThread"); + + _kr = kr; + _rr = rr; + } + + public void setInterval(int interval) { + _interval = interval; + } + + public void run() { + CMS.debug("Inside run method of KeyStatusUpdateThread"); + + while (true) { + try { + // block the update while another thread + // (such as the CRL Update) is running + CMS.debug("About to start checkRanges"); + synchronized (_kr.mKeyStatusUpdateThread) { + CMS.debug("Starting key checkRanges"); + _kr.checkRanges(); + CMS.debug("key checkRanges done"); + + CMS.debug("Starting request checkRanges"); + _rr.checkRanges(); + CMS.debug("request checkRanges done"); + } + } catch (Exception e) { + CMS.debug("key checkRanges done"); + } + try { + sleep(_interval * 1000); + } catch (InterruptedException e) { + } + } + } } diff --git a/pki/base/common/src/com/netscape/cmscore/dbs/ReplicaIDRepository.java b/pki/base/common/src/com/netscape/cmscore/dbs/ReplicaIDRepository.java new file mode 100644 index 000000000..026e54a6a --- /dev/null +++ b/pki/base/common/src/com/netscape/cmscore/dbs/ReplicaIDRepository.java @@ -0,0 +1,82 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is 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; version 2 of the License. +// +// 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, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmscore.dbs; + + +import java.math.BigInteger; + +import com.netscape.certsrv.dbs.*; +import com.netscape.certsrv.dbs.replicadb.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.apps.CMS; + +/** + * A class represents a replica repository. It + * creates unique managed replica IDs. + * <P> + * + * @author alee + * @version $Revision: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +public class ReplicaIDRepository extends Repository + implements IReplicaIDRepository { + + private IDBSubsystem mDBService; + private String mBaseDN; + + /** + * Constructs a certificate repository. + */ + public ReplicaIDRepository(IDBSubsystem dbService, int increment, String baseDN) + throws EDBException { + super(dbService, increment, baseDN); + mBaseDN = baseDN; + mDBService = dbService; + } + + + /** + * Returns last serial number in given range + */ + public BigInteger getLastSerialNumberInRange(BigInteger serial_low_bound, BigInteger serial_upper_bound) + throws EBaseException { + CMS.debug("ReplicaIDReposoitory: in getLastSerialNumberInRange: low " + serial_low_bound + " high " + serial_upper_bound); + if(serial_low_bound == null || serial_upper_bound == null || serial_low_bound.compareTo(serial_upper_bound) >= 0 ) { + return null; + } + BigInteger ret = new BigInteger(getMinSerial()); + if ((ret==null) || (ret.compareTo(serial_upper_bound) >0) || (ret.compareTo(serial_low_bound) <0)) { + return null; + } + return ret; + } + + /** + * Retrieves DN of this repository. + */ + public String getDN() { + return mBaseDN; + } + + /** + * Retrieves backend database handle. + */ + public IDBSubsystem getDBSubsystem() { + return mDBService; + } +} diff --git a/pki/base/common/src/com/netscape/cmscore/dbs/Repository.java b/pki/base/common/src/com/netscape/cmscore/dbs/Repository.java index db7cc707d..86c08b40e 100644 --- a/pki/base/common/src/com/netscape/cmscore/dbs/Repository.java +++ b/pki/base/common/src/com/netscape/cmscore/dbs/Repository.java @@ -29,6 +29,8 @@ import com.netscape.certsrv.dbs.repository.*; import com.netscape.certsrv.dbs.certdb.*; import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.dbs.keydb.*; +import com.netscape.certsrv.dbs.replicadb.*; + /** * A class represents a generic repository. It maintains unique * serial number within repository. @@ -48,6 +50,7 @@ public abstract class Repository implements IRepository { private static final BigInteger BI_ONE = new BigInteger("1"); private BigInteger BI_INCREMENT = null; + private static final BigInteger BI_ZERO = new BigInteger("0"); // (the next serialNo to be issued) - 1 private BigInteger mSerialNo = null; // the serialNo attribute stored in db @@ -55,13 +58,22 @@ public abstract class Repository implements IRepository { private String mMaxSerial = null; private String mMinSerial = null; + private String mNextMaxSerial = null; + private String mNextMinSerial = null; private BigInteger mMinSerialNo = null; private BigInteger mMaxSerialNo = null; + private BigInteger mNextMinSerialNo = null; + private BigInteger mNextMaxSerialNo = null; + + private BigInteger mIncrementNo = null; + private BigInteger mLowWaterMarkNo = null; private IDBSubsystem mDB = null; private String mBaseDN = null; private boolean mInit = false; + private int mRadix = 10; + private int mRepo = -1; private BigInteger mLastSerialNo = null; @@ -121,7 +133,7 @@ public abstract class Repository implements IRepository { * * @return next serial number */ - private BigInteger getSerialNumber() throws EBaseException { + protected BigInteger getSerialNumber() throws EBaseException { IDBSSession s = mDB.createSession(); CMS.debug("Repository: getSerialNumber."); @@ -166,7 +178,7 @@ public abstract class Repository implements IRepository { * * @param num serial number */ - private void setSerialNumber(BigInteger num) throws EBaseException { + protected void setSerialNumber(BigInteger num) throws EBaseException { IDBSSession s = mDB.createSession(); CMS.debug("Repository:setSerialNumber " + num.toString()); @@ -175,17 +187,69 @@ public abstract class Repository implements IRepository { } + /** + * Get the maximum serial number. + * + * @return maximum serial number + */ public String getMaxSerial() { return mMaxSerial; } + /** + * Set the maximum serial number. + * + * @param serial maximum number + * @exception EBaseException failed to set maximum serial number + */ public void setMaxSerial(String serial) throws EBaseException { BigInteger maxSerial = null; - CMS.debug("Repository:setMaxSerial " + serial); + maxSerial = new BigInteger(serial, mRadix); + if (maxSerial != null) { + mMaxSerial = serial; + mMaxSerialNo = maxSerial; + } + } + + /** + * Get the maximum serial number in next range. + * + * @return maximum serial number in next range + */ + public String getNextMaxSerial() { + return mNextMaxSerial; + } + + /** + * Set the maximum serial number in next range + * + * @param serial maximum number in next range + * @exception EBaseException failed to set maximum serial number in next range + */ + public void setNextMaxSerial(String serial) throws EBaseException { + BigInteger maxSerial = null; + CMS.debug("Repository:setNextMaxSerial " + serial); + + maxSerial = new BigInteger(serial, mRadix); + if (maxSerial != null) { + mNextMaxSerial = serial; + mNextMaxSerialNo = maxSerial; + } + return; } + + /** + * Get the minimum serial number. + * + * @return minimum serial number + */ + public String getMinSerial() { + return mMinSerial; + } + /** * init serial number cache @@ -193,47 +257,55 @@ public abstract class Repository implements IRepository { private void initCache() throws EBaseException { mNext = getSerialNumber(); BigInteger serialConfig = new BigInteger("0"); - - int radix = 10; - + mRadix = 10; + CMS.debug("Repository: in InitCache"); - String minSerial = mDB.getMinSerialConfig(); - String maxSerial = mDB.getMaxSerialConfig(); - String minRequest = mDB.getMinRequestConfig(); - String maxRequest = mDB.getMaxRequestConfig(); - - CMS.debug("Repository: minSerial " + minSerial + " maxSerial: " + maxSerial + " minRequest " + minRequest + " maxRequest " + maxRequest); if (this instanceof ICertificateRepository) { - - mMaxSerial = maxSerial; - mMinSerial = minSerial; - radix = 16; CMS.debug("Repository: Instance of Certificate Repository."); - } else { + mRadix = 16; + mRepo = IDBSubsystem.CERTS; + } else if (this instanceof IKeyRepository) { + // Key Repository uses the same configuration parameters as Certificate + // Repository. This is ok because they are on separate subsystems. + CMS.debug("Repository: Instance of Key Repository"); + mRadix = 16; + mRepo = IDBSubsystem.CERTS; + } else if (this instanceof IReplicaIDRepository) { + CMS.debug("Repository: Instance of Replica ID repository"); + mRepo = IDBSubsystem.REPLICA_ID; + } else { + // CRLRepository subclasses this too, but does not use serial number stuff + CMS.debug("Repository: Instance of Request Repository or CRLRepository."); + mRepo = IDBSubsystem.REQUESTS; + } - if(this instanceof IKeyRepository) { + mMinSerial = mDB.getMinSerialConfig(mRepo); + mMaxSerial = mDB.getMaxSerialConfig(mRepo); + mNextMinSerial = mDB.getNextMinSerialConfig(mRepo); + mNextMaxSerial = mDB.getNextMaxSerialConfig(mRepo); + String increment = mDB.getIncrementConfig(mRepo); + String lowWaterMark = mDB.getLowWaterMarkConfig(mRepo); - mMaxSerial = maxSerial; - mMinSerial = minSerial; - radix = 16; - CMS.debug("Repository: Instance of Key Repository."); + CMS.debug("Repository: minSerial " + mMinSerial + " maxSerial: " + mMaxSerial); - } else { // request repository + if(mMinSerial != null) + mMinSerialNo = new BigInteger(mMinSerial,mRadix); - mMaxSerial = maxRequest; - mMinSerial = minRequest; - radix = 10; - CMS.debug("Repository: Instance of Request Repository."); + if(mMaxSerial != null) + mMaxSerialNo = new BigInteger(mMaxSerial,mRadix); - } - } + if(mNextMinSerial != null) + mNextMinSerialNo = new BigInteger(mNextMinSerial,mRadix); - if(mMinSerial != null) - mMinSerialNo = new BigInteger(mMinSerial,radix); + if(mNextMaxSerial != null) + mNextMaxSerialNo = new BigInteger(mNextMaxSerial,mRadix); - if(mMaxSerial != null) - mMaxSerialNo = new BigInteger(mMaxSerial,radix); + if(lowWaterMark != null) + mLowWaterMarkNo = new BigInteger(lowWaterMark,mRadix); + + if(increment != null) + mIncrementNo = new BigInteger(increment,mRadix); BigInteger theSerialNo = null; theSerialNo = getLastSerialNumberInRange(mMinSerialNo,mMaxSerialNo); @@ -278,7 +350,6 @@ public abstract class Repository implements IRepository { // mSerialNo is already set. But just in case CMS.debug("Repository:In setTheSerialNumber " + num.toString()); - if (mLastSerialNo == null) initCache(); @@ -306,6 +377,7 @@ public abstract class Repository implements IRepository { EBaseException { CMS.debug("Repository: in getNextSerialNumber. "); + if (mLastSerialNo == null) { initCache(); @@ -320,10 +392,32 @@ public abstract class Repository implements IRepository { CMS.debug( "Repository::getNextSerialNumber() " + "- mLastSerialNo is null!" ); throw new EBaseException( "mLastSerialNo is null" ); - } else if( mLastSerialNo.compareTo( mMaxSerialNo ) > 0 ) { - mLastSerialNo = mLastSerialNo.subtract(BI_ONE); - throw new EDBException(CMS.getUserMessage("CMS_DBS_LIMIT_REACHED", - mLastSerialNo.toString())); + } + + // check if we have reached the end of the range + // if so, move to next range + if (mLastSerialNo.compareTo( mMaxSerialNo ) > 0 ) { + if (mDB.getEnableSerialMgmt()) { + CMS.debug("Reached the end of the range. Attempting to move to next range"); + mMinSerialNo = mNextMinSerialNo; + mMaxSerialNo = mNextMaxSerialNo; + mLastSerialNo = mMinSerialNo; + mNextMinSerialNo = null; + mNextMaxSerialNo = null; + if ((mMaxSerialNo == null) || (mMinSerialNo == null)) { + throw new EDBException(CMS.getUserMessage("CMS_DBS_LIMIT_REACHED", + mLastSerialNo.toString())); + } + + // persist the changes + mDB.setMinSerialConfig(mRepo, mMinSerialNo.toString()); + mDB.setMaxSerialConfig(mRepo, mMaxSerialNo.toString()); + mDB.setNextMinSerialConfig(mRepo, null); + mDB.setNextMaxSerialConfig(mRepo, null); + } else { + throw new EDBException(CMS.getUserMessage("CMS_DBS_LIMIT_REACHED", + mLastSerialNo.toString())); + } } BigInteger retSerial = new BigInteger(mLastSerialNo.toString()); @@ -332,6 +426,80 @@ public abstract class Repository implements IRepository { return retSerial; } + /** + * Checks to see if a new range is needed, or if we have reached the end of the + * current range, or if a range conflict has occurred. + * + * @exception EBaseException failed to check next range for conflicts + */ + public void checkRanges() throws EBaseException + { + if (!mDB.getEnableSerialMgmt()) { + CMS.debug("Serial Management not enabled. Returning .. "); + return; + } + if (CMS.getEESSLPort() == null) { + CMS.debug("Server not completely started. Returning .."); + return; + } + + if (mLastSerialNo == null) + initCache(); + + BigInteger numsInRange = mMaxSerialNo.subtract(mLastSerialNo); + BigInteger numsInNextRange = null; + BigInteger numsAvail = null; + CMS.debug("Serial numbers left in range: " + numsInRange.toString()); + CMS.debug("Last Serial Number: " + mLastSerialNo.toString()); + if ((mNextMaxSerialNo != null) && (mNextMinSerialNo != null)) { + numsInNextRange = mNextMaxSerialNo.subtract(mNextMinSerialNo); + numsAvail = numsInRange.add(numsInNextRange); + CMS.debug("Serial Numbers in next range: " + numsInNextRange.toString()); + CMS.debug("Serial Numbers available: " + numsAvail.toString()); + } else { + numsAvail = numsInRange; + CMS.debug("Serial Numbers available: " + numsAvail.toString()); + } + + if ((numsAvail.compareTo(mLowWaterMarkNo) < 0) && (!CMS.isPreOpMode()) ) { + CMS.debug("Low water mark reached. Requesting next range"); + mNextMinSerialNo = new BigInteger(mDB.getNextRange(mRepo), mRadix); + if (mNextMinSerialNo == null) { + CMS.debug("Next Range not available"); + } else { + CMS.debug("nNextMinSerialNo has been set to " + mNextMinSerialNo.toString(mRadix)); + mNextMaxSerialNo = mNextMinSerialNo.add(mIncrementNo); + numsAvail = numsAvail.add(mIncrementNo); + mDB.setNextMinSerialConfig(mRepo, mNextMinSerialNo.toString(mRadix)); + mDB.setNextMaxSerialConfig(mRepo, mNextMaxSerialNo.toString(mRadix)); + } + } + + if (numsInRange.compareTo (mLowWaterMarkNo) < 0 ) { + // check for a replication error + CMS.debug("Checking for a range conflict"); + if (mDB.hasRangeConflict(mRepo)) { + CMS.debug("Range Conflict found! Removing next range."); + mNextMaxSerialNo = null; + mNextMinSerialNo= null; + mDB.setNextMinSerialConfig(mRepo, null); + mDB.setNextMaxSerialConfig(mRepo, null); + } + } + } + + /** + * Sets whether serial number management is enabled for certs + * and requests. + * + * @param value true/false + * @exception EBaseException failed to set + */ + public void setEnableSerialMgmt(boolean value) throws EBaseException + { + mDB.setEnableSerialMgmt(value); + } + public abstract BigInteger getLastSerialNumberInRange(BigInteger serial_low_bound, BigInteger serial_upper_bound) throws EBaseException; } |