com.phloc.commons.idfactory.AbstractPersistingLongIDFactory Maven / Gradle / Ivy
/**
* Copyright (C) 2006-2014 phloc systems
* http://www.phloc.com
* office[at]phloc[dot]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 com.phloc.commons.idfactory;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.Nonnegative;
import javax.annotation.concurrent.ThreadSafe;
import com.phloc.commons.ValueEnforcer;
import com.phloc.commons.hash.HashCodeGenerator;
import com.phloc.commons.string.ToStringGenerator;
/**
* This implementation of {@link ILongIDFactory} reads IDs from a device. It
* does it by reserving a range of n IDs so that not each ID
* reservation requires IO. If only 1 ID is effectively used, the other
* n-1 IDs are lost and will never be assigned to any object again.
*
* @author Philip Helger
*/
@ThreadSafe
public abstract class AbstractPersistingLongIDFactory implements ILongIDFactory
{
private final Lock m_aLock = new ReentrantLock ();
private final int m_nReserveCount;
private long m_nID = 0L;
private long m_nLastID = -1L;
public AbstractPersistingLongIDFactory (@Nonnegative final int nReserveCount)
{
ValueEnforcer.isGT0 (nReserveCount, "ReserveCount");
m_nReserveCount = nReserveCount;
}
@Nonnegative
protected final int getReserveCount ()
{
// As reserve count is final, we don't need to lock access to it!
return m_nReserveCount;
}
/**
* Read the current ID from the device. In case the method is called for a
* non-initialized device, 0 should be returned.
* The update should write the read value plus the passed reserve count back
* to the device. This method should perform an atomic read and update to
* avoid that ID can be reused.
* Pseudo code:
*
*
* protected long readAndUpdateIDCounter (int nReserveCount)
* {
* final long nRead = FileIO.read (file);
* FileIO.write (file, nRead + nReserveCount);
* return nRead;
* }
*
*
* @param nReserveCount
* the number that should be added to the read value. Always > 0.
* @return 0 if this method is called for a non-initialized device, the value
* read from the device otherwise or
* {@link com.phloc.commons.CGlobal#ILLEGAL_ULONG} in case of an
* error.
*/
protected abstract long readAndUpdateIDCounter (@Nonnegative int nReserveCount);
/*
* Note: this implementation must be synchronized because the method calling
* this only uses a readLock!
*/
public final long getNewID ()
{
m_aLock.lock ();
try
{
if (m_nID >= m_nLastID)
{
// Read new IDs
final long nNewID = readAndUpdateIDCounter (m_nReserveCount);
// the existing ID may not be < than the previously used ID!
if (m_nLastID >= 0 && nNewID < m_nID)
throw new IllegalStateException ("The read value " +
nNewID +
" is smaller than the last known ID " +
m_nID +
"!");
m_nID = nNewID;
m_nLastID = nNewID + m_nReserveCount;
}
return m_nID++;
}
finally
{
m_aLock.unlock ();
}
}
@Override
public boolean equals (final Object o)
{
if (o == this)
return true;
if (o == null || !getClass ().equals (o.getClass ()))
return false;
final AbstractPersistingLongIDFactory rhs = (AbstractPersistingLongIDFactory) o;
return m_nReserveCount == rhs.m_nReserveCount && m_nID == rhs.m_nID;
}
@Override
public int hashCode ()
{
return new HashCodeGenerator (this).append (m_nReserveCount).append (m_nID).getHashCode ();
}
@Override
public String toString ()
{
return new ToStringGenerator (this).append ("reserveCount", m_nReserveCount)
.append ("ID", m_nID)
.append ("lastID", m_nLastID)
.toString ();
}
}