/* * Created on 20-Sep-2005 * * ALMA - Atacama Large Millimiter Array * (c) European Southern Observatory, 2002 * Copyright by ESO (in the framework of the ALMA collaboration), * All rights reserved * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ package alma.acs.container.archive; import java.net.URI; import java.util.HashMap; import java.util.logging.Level; import java.util.logging.Logger; import alma.ArchiveIdentifierError.wrappers.AcsJIdentifierUnavailableEx; import alma.ArchiveIdentifierError.wrappers.AcsJIdentifierUnexpectedEx; import alma.ArchiveIdentifierError.wrappers.AcsJRangeExhaustedEx; import alma.ArchiveIdentifierError.wrappers.AcsJRangeLockedEx; import alma.ArchiveIdentifierError.wrappers.AcsJRangeUnavailableEx; import alma.ArchiveIdentifierError.wrappers.AcsJRangeUnlockedEx; import alma.ArchiveIdentifierError.wrappers.AcsJUidAlreadyExistsEx; import alma.archive.range.IdentifierRange; import alma.entities.commonentity.EntityRefT; import alma.entities.commonentity.EntityT; import alma.xmlstore.IdentifierJ; import alma.xmlstore.IdentifierPackage.NotAvailable; import alma.xmlstore.IdentifierPackage.NotFound; /** * Library-style class that facilitates the use of UID ranges obtained from the identifier archive. *
Range
objects by only referring to them through their UIDs
* in methods {@link #assignUniqueEntityId(EntityT, URI)}, {@link #assignUniqueEntityRef(EntityRefT, URI)} etc., but it is not clear how useful this is.
* @author simon, hsommer
*/
public class UIDLibrary
{
private final Logger logger;
private static volatile Range defaultRange;
private final HashMapEntityT
castor class
* of an XML-based entity such as a SchedBlock.
*
* Implementation note: the default range of UIDs is retrieved from the archive at the first call and is then shared among instances
* in order to be frugal on UIDs and to minimize archive access.
* This means that often the passed in identifier
will not be used at all but still must be provided,
* because the calling component can not know whether another component or the container has
* called this method before.
* This method is synchronized to avoid the very unlikely situation that defaultRange.hasNextId
succeeds for one thread but then later
* assigning the UID still fails because of another thread having stolen the last free UID in the meantime.
*
* @param identifier the identifier archive from which a new Range
can be obtained if necessary. Use
* ContainerServices#getTransparentXmlComponent(IdentifierJ.class, identRaw, IdentifierOperations.class);
* to create the required XML binding class aware interface from the plain-Corba Identifier
object
* (identRaw
) that is obtained, for example, by
* IdentifierHelper.narrow(getDefaultComponent("IDL:alma/xmlstore/Identifier:1.0"))
.
* @see ContainerServices#assignUniqueEntityId(EntityT)
*/
public synchronized void assignUniqueEntityId(EntityT entity, IdentifierJ identifier)
throws AcsJUidAlreadyExistsEx, AcsJIdentifierUnavailableEx, AcsJRangeUnavailableEx, AcsJIdentifierUnexpectedEx {
try {
checkDefaultRange(identifier);
defaultRange.assignUniqueEntityId(entity);
if (logger.isLoggable(Level.FINEST)) {
logger.finest("Assigned UID '" + entity.getEntityId() + "' to entity of type " + entity.getEntityTypeName());
}
} catch (AcsJUidAlreadyExistsEx e) {
throw e;
} catch (AcsJRangeUnavailableEx e) {
throw e;
} catch (AcsJIdentifierUnavailableEx e) {
throw e;
} catch (Throwable e) {
// AcsJRangeLockedEx and AcsJRangeExhaustedEx should not occur for default range, thanks to method checkDefaultRange
throw new AcsJIdentifierUnexpectedEx(e);
}
}
/**
* Similar to {@link #assignUniqueEntityId(EntityT, IdentifierJ)}, but allows replacing an existing ID.
* Only in very special cases such as ObsPrep replacing locally-generated IDs with archive-generated UIDs
* should this method be used. Replacing UIDs can easily corrupt the archive because existing links would no longer hold!
* @param identifier the identifier archive from which a new Range
can be obtained if necessary. Use
* ContainerServices#getTransparentXmlComponent(IdentifierJ.class, identRaw, IdentifierOperations.class);
* to create the required XML binding class aware interface from the plain-Corba Identifier
object
* (identRaw
) that is obtained, for example, by
* IdentifierHelper.narrow(getDefaultComponent("IDL:alma/xmlstore/Identifier:1.0"))
.
* @see #assignUniqueEntityId(EntityT, IdentifierJ)
*/
public synchronized void replaceUniqueEntityId(EntityT entity, IdentifierJ identifier)
throws AcsJRangeUnavailableEx, AcsJIdentifierUnavailableEx, AcsJIdentifierUnexpectedEx {
if (entity == null) {
throw new NullPointerException("argument 'entity' must not be null.");
}
try {
String oldUid = entity.getEntityId();
checkDefaultRange(identifier);
defaultRange.replaceUniqueEntityId(entity);
logger.info("Replaced old UID '" + oldUid + "' with new UID '" + entity.getEntityId() + "' on an entity of type " + entity.getEntityTypeName());
} catch (AcsJRangeUnavailableEx e) {
throw e;
} catch (AcsJIdentifierUnavailableEx e) {
throw e;
} catch (Throwable e) {
// AcsJRangeLockedEx and AcsJRangeExhaustedEx should not occur for default range, thanks to method checkDefaultRange
throw new AcsJIdentifierUnexpectedEx(e);
}
}
/**
* Creates a default range on demand, or sets a new default range if the old range has no more UID
* (which happens after pulling Long.MAX UIDs, or sooner if a limit was set).
* @param identifier the identifier archive from which a new Range
can be obtained if necessary. Use
* ContainerServices#getTransparentXmlComponent(IdentifierJ.class, identRaw, IdentifierOperations.class);
* to create the required XML binding class aware interface from the plain-Corba Identifier
object
* (identRaw
) that is obtained, for example, by
* IdentifierHelper.narrow(getDefaultComponent("IDL:alma/xmlstore/Identifier:1.0"))
.
*/
protected synchronized void checkDefaultRange(IdentifierJ identifier) throws AcsJRangeUnavailableEx, AcsJIdentifierUnavailableEx {
if (identifier == null) {
AcsJIdentifierUnavailableEx ex = new AcsJIdentifierUnavailableEx();
ex.setContextInfo("Provided identifier reference is null.");
throw ex;
}
try {
if (defaultRange == null || !defaultRange.hasNextID()) {
defaultRange = new Range(identifier.getNewRange());
}
} catch (NotAvailable e) {
AcsJRangeUnavailableEx ex = new AcsJRangeUnavailableEx();
ex.setRange("default");
throw ex;
}
}
/**
* Fetches a new restricted range.
* This will return a URI allowing access to the new Range.
* The range is automatically stored in the archive.
* This method should only be used in very special cases,
* see Archive/UidLibrary wiki page!
* @param identifier the identifier archive from which a new Range
can be obtained if necessary. Use
* ContainerServices#getTransparentXmlComponent(IdentifierJ.class, identRaw, IdentifierOperations.class);
* to create the required XML binding class aware interface from the plain-Corba Identifier
object
* (identRaw
) that is obtained, for example, by
* IdentifierHelper.narrow(getDefaultComponent("IDL:alma/xmlstore/Identifier:1.0"))
.
* @param printLogs Emit logs, iff set to true.
* @return the UID of the range, which can be used for example as an argument in {@link #assignUniqueEntityId(EntityT, URI)}.
* @throws UniqueIdException if the range cannot be obtained from the archive.
*/
public URI getNewRestrictedRange(int size, String user, IdentifierJ identifier, boolean printLogs) throws AcsJRangeUnavailableEx, AcsJIdentifierUnexpectedEx {
Range range = null;
try
{
if (printLogs) logger.finest("UIDLibrary: Fetching a restricted range");
IdentifierRange idRange = identifier.getNewRestrictedRange(size, user);
range = new Range(idRange);
}
catch (NotAvailable e) {
throw new AcsJRangeUnavailableEx(e);
}
URI uri = range.rangeId();
if (idRanges.containsKey(uri)) {
AcsJIdentifierUnexpectedEx ex = new AcsJIdentifierUnexpectedEx();
ex.setContextInfo("Cannot store new range. URI occupied. This should never have happened by design!!");
throw ex;
}
if (printLogs) logger.finest("UIDLibrary: Storing Range under: "+ uri.toASCIIString());
idRanges.put(uri,range);
return uri;
}
/**
* convenience method for the above, printLogs is set to true
**/
public URI getNewRestrictedRange(int size, String user, IdentifierJ identifier) throws AcsJRangeUnavailableEx, AcsJIdentifierUnexpectedEx {
return getNewRestrictedRange(size, user, identifier, true);
}
/**
* Assigns a uid to the EntityT from the specified range.
* This is not permitted with a locked Range object.
* This method should only be used in very special cases,
* see Archive/UidLibrary wiki page!
*
* TODO: figure out if this is meant to work only if the Range referenced by uri has been loaded previously by this instance.
* If so, put a comment that fetchRange must be called first. Otherwise the fetch method could be called automatically.
*
* @param entity
* @param uri the UID of the Range object
* @param printLogs set to true, iff logs should be printed
*/
public void assignUniqueEntityId(EntityT entity, URI uri, boolean printLogs) throws AcsJRangeUnavailableEx, AcsJUidAlreadyExistsEx, AcsJRangeLockedEx, AcsJRangeExhaustedEx
{
if (idRanges.containsKey(uri)){
if (printLogs && logger.isLoggable(Level.FINEST)) {
logger.finest("UIDLibrary: Assigning ID to entity from range " + uri.toASCIIString());
}
Range r = idRanges.get(uri);
r.assignUniqueEntityId(entity);
}
else{
AcsJRangeUnavailableEx ex = new AcsJRangeUnavailableEx();
ex.setRange(uri.toASCIIString());
throw ex;
}
}
/**
* convenience method for the above, printLogs is set to true
**/
public void assignUniqueEntityId(EntityT entity, URI uri) throws AcsJRangeUnavailableEx, AcsJUidAlreadyExistsEx, AcsJRangeLockedEx, AcsJRangeExhaustedEx {
assignUniqueEntityId(entity, uri, true);
}
/**
* Fetch an existing range from the archive and deserialise, only certain
* operations will be permitted.
* This method should only be used in very special cases,
* see Archive/UidLibrary wiki page!
* @param uri
* @param identifier the identifier archive from which a new Range
can be obtained if necessary. Use
* ContainerServices#getTransparentXmlComponent(IdentifierJ.class, identRaw, IdentifierOperations.class);
* to create the required XML binding class aware interface from the plain-Corba Identifier
object
* (identRaw
) that is obtained, for example, by
* IdentifierHelper.narrow(getDefaultComponent("IDL:alma/xmlstore/Identifier:1.0"))
.
*
* @throws AcsJRangeUnavailableEx
*/
public void fetchRange(URI uri, String user, IdentifierJ identifier)
throws AcsJRangeUnavailableEx
{
IdentifierRange idRange = null;
try{
logger.finest("UIDLibrary: Fetching range: "
+ uri.toASCIIString());
idRange = identifier.getExistingRange(uri.toASCIIString(),user);
}
catch (NotFound e) {
throw new AcsJRangeUnavailableEx(e);
}
Range r = new Range(idRange);
if (!refRanges.containsKey(uri)){
refRanges.put(uri,r);
}
else {
AcsJRangeUnavailableEx ex = new AcsJRangeUnavailableEx();
ex.setRange(uri.toASCIIString());
throw ex;
}
}
/**
* Assigns a UID to an entity reference.
* This method should only be used in very special cases,
* see Archive/UidLibrary wiki page!
* Note that this operation is only permitted with a locked range.
* @param ref the schema-generated entity reference
* @param uri
* @throws UniqueIdException
*/
public void assignUniqueEntityRef(EntityRefT ref, URI uri)
throws AcsJRangeUnavailableEx, AcsJRangeExhaustedEx, AcsJRangeUnlockedEx
{
if (refRanges.containsKey(uri)){
logger.finest("UIDLibrary: Assigning ID Ref to entity from: " + uri.toASCIIString());
Range r = refRanges.get(uri);
r.assignUniqueEntityRef(ref);
}
else{
AcsJRangeUnavailableEx ex = new AcsJRangeUnavailableEx();
ex.setRange(uri.toASCIIString());
throw ex;
}
}
}