/** * Copyright (c) 2008-2011 Sonatype, Inc. * All rights reserved. Includes the third-party code listed at http://www.sonatype.com/products/nexus/attributions. * * This program is free software: you can redistribute it and/or modify it only under the terms of the GNU Affero General * Public License Version 3 as published by the Free Software Foundation. * * 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 Affero General Public License Version 3 * for more details. * * You should have received a copy of the GNU Affero General Public License Version 3 along with this program. If not, see * http://www.gnu.org/licenses. * * Sonatype Nexus (TM) Open Source Version is available from Sonatype, Inc. Sonatype and Sonatype Nexus are trademarks of * Sonatype, Inc. Apache Maven is a trademark of the Apache Foundation. M2Eclipse is a trademark of the Eclipse Foundation. * All other trademarks are the property of their respective owners. */ package org.sonatype.nexus.proxy.item; import java.lang.ref.WeakReference; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.locks.ReentrantLock; import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.util.StringUtils; import org.sonatype.nexus.proxy.NoSuchRepositoryException; import org.sonatype.nexus.proxy.registry.RepositoryRegistry; import org.sonatype.nexus.proxy.repository.Repository; /** * A default factory for UIDs. * * @author cstamas */ @Component( role = RepositoryItemUidFactory.class ) public class DefaultRepositoryItemUidFactory implements RepositoryItemUidFactory { /** * The registry. */ @Requirement private RepositoryRegistry repositoryRegistry; private final HashMap<String, WeakReference<RepositoryItemUid>> itemUidMap = new HashMap<String, WeakReference<RepositoryItemUid>>(); private final ReentrantLock uidCreateLock = new ReentrantLock(); public RepositoryItemUid createUid( Repository repository, String path ) { // path corrections if ( !StringUtils.isEmpty( path ) ) { if ( !path.startsWith( RepositoryItemUid.PATH_ROOT ) ) { path = RepositoryItemUid.PATH_ROOT + path; } } else { path = RepositoryItemUid.PATH_ROOT; } final String key = repository.getId() + ":" + path; RepositoryItemUid toBeReturned = null; uidCreateLock.lock(); try { // try to get it 1st WeakReference<RepositoryItemUid> ref = itemUidMap.get( key ); if ( ref != null ) { toBeReturned = ref.get(); if ( toBeReturned != null ) { cleanUpItemUidMap( false ); return toBeReturned; } } // not here, then put it RepositoryItemUid newGuy = new DefaultRepositoryItemUid( this, repository, path ); itemUidMap.put( key, new WeakReference<RepositoryItemUid>( newGuy ) ); return newGuy; } finally { uidCreateLock.unlock(); } } public RepositoryItemUid createUid( String uidStr ) throws IllegalArgumentException, NoSuchRepositoryException { if ( uidStr.indexOf( ":" ) > -1 ) { String[] parts = uidStr.split( ":" ); if ( parts.length == 2 ) { Repository repository = repositoryRegistry.getRepository( parts[0] ); return createUid( repository, parts[1] ); } else { throw new IllegalArgumentException( uidStr + " is malformed RepositoryItemUid! The proper format is '<repoId>:/path/to/something'." ); } } else { throw new IllegalArgumentException( uidStr + " is malformed RepositoryItemUid! The proper format is '<repoId>:/path/to/something'." ); } } public Map<String, RepositoryItemUid> getActiveUidMapSnapshot() { return Collections.emptyMap(); } /** * Used in UTs only, NOT public method! This method call should be called from UTs only, not from production code * since it interferes with locking! * * @return */ public int getUidCount( boolean forceClean ) { if ( forceClean ) { cleanUpItemUidMap( true ); } return itemUidMap.size(); } // == // =v=v=v=v=v=v= This part here probably needs polishing: the retention should depend on load too =v=v=v=v=v=v= private static final long ITEM_UID_MAP_RETENTION_TIME = 5000; private volatile long lastClearedItemUidMap; private final ReentrantLock cleanupLock = new ReentrantLock(); private void cleanUpItemUidMap( boolean force ) { // just try to lock it. If we cannot lock it, leave it, since some other thread is // already doing cleanup. But even if we do succeed in locking the cleanupLock, we have the // time barrier, that have to be true, to do actual cleanup. if ( cleanupLock.tryLock() ) { // we are in, that means no one else is doing cleanup, and also that // we acquired the lock in the same time. // still, it is undecided yet, will we actually perform the cleanup, since // we have a time barrier to pass also (unless force=true). try { long now = System.currentTimeMillis(); if ( force || ( now - lastClearedItemUidMap > ITEM_UID_MAP_RETENTION_TIME ) ) { lastClearedItemUidMap = now; for ( Iterator<ConcurrentMap.Entry<String, WeakReference<RepositoryItemUid>>> i = itemUidMap.entrySet().iterator(); i.hasNext(); ) { ConcurrentMap.Entry<String, WeakReference<RepositoryItemUid>> entry = i.next(); if ( entry.getValue().get() == null ) { i.remove(); } } } } finally { cleanupLock.unlock(); } } } // =^=^=^=^=^=^= This part here probably needs polishing: the retention should depend on load too =^=^=^=^=^=^= }