/** * Copyright (c) 2000-present Liferay, Inc. 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. */ package com.liferay.portal.kernel.concurrent; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.memory.FinalizeAction; import com.liferay.portal.kernel.memory.FinalizeManager; import com.liferay.portal.kernel.util.ReflectionUtil; import java.lang.ref.Reference; import java.lang.reflect.Field; import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Future; /** * @author Shuyang Zhou */ public class AsyncBroker<K, V> { public Map<K, NoticeableFuture<V>> getOpenBids() { return Collections.<K, NoticeableFuture<V>>unmodifiableMap( _defaultNoticeableFutures); } public NoticeableFuture<V> post(K key) { DefaultNoticeableFuture<V> defaultNoticeableFuture = new DefaultNoticeableFuture<>(); NoticeableFuture<V> previousNoticeableFuture = post( key, defaultNoticeableFuture); if (previousNoticeableFuture == null) { return defaultNoticeableFuture; } return previousNoticeableFuture; } public NoticeableFuture<V> post( final K key, final DefaultNoticeableFuture<V> defaultNoticeableFuture) { DefaultNoticeableFuture<V> previousDefaultNoticeableFuture = _defaultNoticeableFutures.putIfAbsent(key, defaultNoticeableFuture); if (previousDefaultNoticeableFuture != null) { return previousDefaultNoticeableFuture; } defaultNoticeableFuture.addFutureListener( new FutureListener<V>() { @Override public void complete(Future<V> future) { _defaultNoticeableFutures.remove( key, defaultNoticeableFuture); } }); if (_REFERENT_FIELD != null) { FinalizeManager.register( defaultNoticeableFuture, new CancellationFinalizeAction(key), FinalizeManager.PHANTOM_REFERENCE_FACTORY); } return null; } public NoticeableFuture<V> take(K key) { return _defaultNoticeableFutures.remove(key); } public boolean takeWithException(K key, Throwable throwable) { DefaultNoticeableFuture<V> defaultNoticeableFuture = _defaultNoticeableFutures.remove(key); if (defaultNoticeableFuture == null) { return false; } defaultNoticeableFuture.setException(throwable); return true; } public boolean takeWithResult(K key, V result) { DefaultNoticeableFuture<V> defaultNoticeableFuture = _defaultNoticeableFutures.remove(key); if (defaultNoticeableFuture == null) { return false; } defaultNoticeableFuture.set(result); return true; } private static final Field _REFERENT_FIELD; private static final Log _log = LogFactoryUtil.getLog(AsyncBroker.class); static { Field referentField = null; try { referentField = ReflectionUtil.getDeclaredField( Reference.class, "referent"); } catch (Throwable t) { if (_log.isWarnEnabled()) { _log.warn( "Cancellation of orphaned noticeable futures is disabled " + "because the JVM does not support phantom reference " + "resurrection", t); } } _REFERENT_FIELD = referentField; } private final ConcurrentMap<K, DefaultNoticeableFuture<V>> _defaultNoticeableFutures = new ConcurrentReferenceValueHashMap<>( FinalizeManager.WEAK_REFERENCE_FACTORY); private static class CancellationFinalizeAction implements FinalizeAction { public CancellationFinalizeAction(Object key) { _key = key; } @Override public void doFinalize(final Reference<?> reference) { try { NoticeableFuture<?> noticeableFuture = (NoticeableFuture<?>)_REFERENT_FIELD.get(reference); if (noticeableFuture.cancel(true) && _log.isWarnEnabled()) { _log.warn( "Cancelled orphan noticeable future " + noticeableFuture + " with key " + _key); } } catch (Exception e) { _log.error("Unable to access referent of " + reference, e); } } private final Object _key; } }