/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This 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 software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.osgi.service;
import java.util.Stack;
import java.util.concurrent.TimeUnit;
import org.jboss.msc.service.StartContext;
import org.jboss.osgi.framework.spi.LockManager;
import org.jboss.osgi.framework.spi.LockManagerPlugin;
import org.jboss.osgi.spi.Attachable;
import org.jboss.osgi.spi.AttachmentKey;
/**
* An integation plugin for the {@link LockManager}.
*
* @author thomas.diesler@jboss.com
* @since 11-Apr-2013
*/
public final class LockManagerIntegration extends LockManagerPlugin {
@SuppressWarnings("rawtypes")
private static AttachmentKey<Stack> LOCK_CONTEXT_KEY = AttachmentKey.create(Stack.class);
@Override
protected LockManager createServiceValue(StartContext startContext) {
final LockManager delegate = super.createServiceValue(startContext);
return new LockManager() {
@Override
public <T extends LockableItem> T getItemForType(Class<T> type) {
return delegate.getItemForType(type);
}
@Override
public LockContext getCurrentLockContext() {
return delegate.getCurrentLockContext();
}
@Override
public LockContext lockItems(Method method, LockableItem... items) {
LockContext context = null;
if (!skipLocking(method, items)) {
context = delegate.lockItems(method, items);
pushAttachedLockContext(context, items);
}
return context;
}
@Override
public LockContext lockItems(Method method, long timeout, TimeUnit unit, LockableItem... items) {
LockContext context = null;
if (!skipLocking(method, items)) {
context = delegate.lockItems(method, timeout, unit, items);
pushAttachedLockContext(context, items);
}
return context;
}
@Override
public void unlockItems(LockContext context) {
popAttachedLockContext(context);
delegate.unlockItems(context);
}
@SuppressWarnings("unchecked")
private synchronized boolean skipLocking(Method method, LockableItem... items) {
// Another thread might qualify to skip locking
// #1 There must be no current context associated with the thread
if (getCurrentLockContext() != null)
return false;
LockContext context = null;
// #2 All items must have have same context attached
for (LockableItem item : items) {
if (item instanceof Attachable) {
Attachable attachableItem = (Attachable) item;
Stack<LockContext> stack = attachableItem.getAttachment(LOCK_CONTEXT_KEY);
LockContext aux = stack != null && !stack.isEmpty() ? stack.peek() : null;
if (context == null && aux != null) {
context = aux;
}
if (context != aux) {
return false;
}
}
}
// #3 There must be an attached context
if (context == null)
return false;
// Skip the lock when UPDATE started another thread
if (context.getMethod() == Method.UPDATE) {
if (method == Method.STOP || method == Method.INSTALL || method == Method.START) {
return true;
}
}
// Skip the lock when REFRESH started another thread
if (context.getMethod() == Method.REFRESH) {
if (method == Method.STOP || method == Method.UNINSTALL || method == Method.INSTALL || method == Method.START) {
return true;
}
}
// Skip the lock when UNINSTALL started another thread
if (context.getMethod() == Method.UNINSTALL) {
if (method == Method.STOP || method == Method.UNINSTALL) {
return true;
}
}
return false;
}
@SuppressWarnings("unchecked")
private synchronized void pushAttachedLockContext(LockContext context, LockableItem... items) {
for (LockableItem item : items) {
if (item instanceof Attachable) {
Attachable attachableItem = (Attachable) item;
Stack<LockContext> stack = attachableItem.getAttachment(LOCK_CONTEXT_KEY);
if (stack == null) {
stack = new Stack<LockContext>();
attachableItem.putAttachment(LOCK_CONTEXT_KEY, stack);
}
stack.push(context);
}
}
}
private synchronized void popAttachedLockContext(LockContext context) {
if (context != null) {
for (LockableItem item : context.getItems()) {
if (item instanceof Attachable) {
Attachable attachableItem = (Attachable) item;
Stack<?> stack = attachableItem.getAttachment(LOCK_CONTEXT_KEY);
if (stack.size() == 1) {
attachableItem.removeAttachment(LOCK_CONTEXT_KEY);
} else {
stack.pop();
}
}
}
}
}
};
}
}