Skip to content

Commit

Permalink
Add Lock annotation and Lock interceptor
Browse files Browse the repository at this point in the history
Also move extension to the main package since it handles multiple
concerns now.

Signed-off-by: Arjan Tijms <arjan.tijms@gmail.com>
  • Loading branch information
arjantijms committed Mar 5, 2023
1 parent 0a8a113 commit 0ed842f
Show file tree
Hide file tree
Showing 8 changed files with 374 additions and 11 deletions.
9 changes: 6 additions & 3 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import org.omnifaces.services.pooled.PooledExtension;
import org.omnifaces.services.CdiExtension;

import jakarta.enterprise.inject.spi.Extension;

/*
* Copyright 2021 OmniFaces
* Copyright 2021, 2023 OmniFaces
*
* 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
Expand All @@ -19,7 +19,7 @@
*/
module org.omnifaces.services {

provides Extension with PooledExtension;
provides Extension with CdiExtension;

exports org.omnifaces.services;
opens org.omnifaces.services;
Expand All @@ -30,6 +30,9 @@
exports org.omnifaces.services.pooled;
opens org.omnifaces.services.pooled;

exports org.omnifaces.services.lock;
opens org.omnifaces.services.lock;

exports org.omnifaces.services.util;
opens org.omnifaces.services.util;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021, 2022 OmniFaces
* Copyright 2021, 2023 OmniFaces
*
* 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
Expand All @@ -10,12 +10,17 @@
* 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 org.omnifaces.services.pooled;
package org.omnifaces.services;

import static org.omnifaces.utils.annotation.Annotations.createAnnotationInstance;

import org.omnifaces.services.asynchronous.AsynchronousInterceptor;
import org.omnifaces.services.asynchronous.ExecutorBean;
import org.omnifaces.services.lock.Lock;
import org.omnifaces.services.lock.LockInterceptor;
import org.omnifaces.services.pooled.Pooled;
import org.omnifaces.services.pooled.PooledContext;
import org.omnifaces.services.pooled.PooledScopeEnabled;
import org.omnifaces.services.util.AnnotatedTypeWrapper;

import jakarta.ejb.Asynchronous;
Expand All @@ -28,17 +33,22 @@
import jakarta.enterprise.inject.spi.ProcessAnnotatedType;
import jakarta.enterprise.inject.spi.ProcessBean;

public class PooledExtension implements Extension {
public class CdiExtension implements Extension {

private final PooledContext pooledContext = new PooledContext();

public void beforeBeanDiscovery(@Observes BeforeBeanDiscovery beforeBeanDiscovery, BeanManager beanManager) {
addAnnotatedTypes(beforeBeanDiscovery, beanManager,
Asynchronous.class,
AsynchronousInterceptor.class,

Lock.class,
LockInterceptor.class,

ExecutorBean.class);

beforeBeanDiscovery.addScope(Pooled.class, true, false);
beforeBeanDiscovery.addScope(
Pooled.class, true, false);
}

public <T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> processAnnotatedType) {
Expand Down
146 changes: 146 additions & 0 deletions src/main/java/org/omnifaces/services/lock/Lock.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* Copyright 2021, 2023 OmniFaces
*
* 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
*
* https://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 org.omnifaces.services.lock;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.omnifaces.services.lock.Lock.TimeoutType.TIMEOUT;
import static org.omnifaces.services.lock.Lock.Type.WRITE;

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

import jakarta.enterprise.util.AnnotationLiteral;
import jakarta.enterprise.util.Nonbinding;
import jakarta.interceptor.InterceptorBinding;

@InterceptorBinding
@Target({ METHOD, TYPE })
@Retention(RUNTIME)
@Inherited
public @interface Lock {

enum Type {
/**
* For read-only operations. Allows simultaneous access to methods designated as <code>READ</code>, as long as no
* <code>WRITE</code> lock is held.
*/
READ,

/**
* For exclusive access to the bean instance. A <code>WRITE</code> lock can only be acquired when no other method with
* either a <code>READ</code> or <code>WRITE</code> lock is currently held.
*/
WRITE
}

enum TimeoutType {
/**
* A timeout value in the units specified by the <code>unit</code> element.
*/
TIMEOUT,

/**
* Concurrent access is not permitted; so no wait therefore no timeout is allowed.
* Either the lock is available and grabbed immediately, or an exception is thrown.
*/
NOT_PERMITTED,

/**
* The client request will block indefinitely until it can proceed; it waits for as long
* as it needs to, hence the timeout is unlimited.
*/
INDEFINITTE
}

/**
*
* @return The Lock.Type to use
*/
@Nonbinding Type type() default WRITE;

/**
*
* @return the way to deal with time out waiting for a lock
*/
@Nonbinding TimeoutType timeoutType() default TimeoutType.TIMEOUT;

/**
*
* @return the time to wait to obtain a lock
*/
@Nonbinding long accessTimeout() default 60;

/**
*
* @return units used for the specified accessTimeout value.
*/
@Nonbinding TimeUnit unit() default SECONDS;


/**
* Supports inline instantiation of the {@link Lock} annotation.
*
*/
public static final class Literal extends AnnotationLiteral<Lock> implements Lock {

private static final long serialVersionUID = 1L;

/**
* Instance of the Literal annotation.
*/
public static final Literal INSTANCE = of(WRITE, TIMEOUT, 60, SECONDS);

private final Type type;
private final TimeoutType timeoutType;
private final long accessTimeout;
private final TimeUnit unit;

public static Literal of(Type type, TimeoutType timeoutType, long accessTimeout, TimeUnit unit) {
return new Literal(type, timeoutType, accessTimeout, unit);
}

private Literal(Type type, TimeoutType timeoutType, long accessTimeout, TimeUnit unit) {
this.type = type;
this.timeoutType = timeoutType;
this.accessTimeout = accessTimeout;
this.unit = unit;
}

@Override
public Type type() {
return type;
}

@Override
public TimeoutType timeoutType() {
return timeoutType;
}

@Override
public long accessTimeout() {
return accessTimeout;
}

@Override
public TimeUnit unit() {
return unit;
}
}


}
93 changes: 93 additions & 0 deletions src/main/java/org/omnifaces/services/lock/LockInterceptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright 2021, 2023 OmniFaces
*
* 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
*
* https://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 org.omnifaces.services.lock;

import static jakarta.interceptor.Interceptor.Priority.PLATFORM_BEFORE;
import static org.omnifaces.services.lock.Lock.Type.READ;

import java.io.Serializable;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.omnifaces.services.util.CdiUtils;

import jakarta.annotation.Priority;
import jakarta.enterprise.inject.Intercepted;
import jakarta.enterprise.inject.spi.Bean;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.inject.Inject;
import jakarta.interceptor.AroundInvoke;
import jakarta.interceptor.Interceptor;
import jakarta.interceptor.InvocationContext;

@Interceptor
@Lock
@Priority(PLATFORM_BEFORE)
public class LockInterceptor implements Serializable {

private static final long serialVersionUID = 1L;

@Inject
private BeanManager beanManager;

@Inject
@Intercepted
private Bean<?> interceptedBean;

private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

@AroundInvoke
public Object doLock(InvocationContext ctx) throws Exception {
Lock lockAnnotation = getLockAnnotation(ctx);
java.util.concurrent.locks.Lock lock = getReadOrWriteLock(lockAnnotation);

acquireLock(lockAnnotation, lock);
try {
return ctx.proceed();
} finally {
lock.unlock();
}
}

private java.util.concurrent.locks.Lock getReadOrWriteLock(Lock lockAnnotation) {
return lockAnnotation.type() == READ? readWriteLock.readLock() : readWriteLock.writeLock();
}

private Lock getLockAnnotation(InvocationContext ctx) {
return CdiUtils.getInterceptorBindingAnnotation(ctx, beanManager, interceptedBean, Lock.class);
}

private void acquireLock(Lock lockAnnotation, java.util.concurrent.locks.Lock lock) throws InterruptedException {
switch (lockAnnotation.timeoutType()) {
case TIMEOUT:
if (!lock.tryLock(lockAnnotation.accessTimeout(), lockAnnotation.unit())) {
throw new IllegalStateException(
"Could not obtain lock in " +
lockAnnotation.accessTimeout() + " " +
lockAnnotation.unit().name());
}
break;

case INDEFINITTE:
lock.lock();
break;

case NOT_PERMITTED:
if (!lock.tryLock()) {
throw new IllegalStateException("Lock already locked, and no wait allowed");
}
break;
}
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021 OmniFaces
* Copyright 2021, 2023 OmniFaces
*
* 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
Expand Down Expand Up @@ -96,7 +96,7 @@ boolean hasAllocatedInstanceOf(Bean<?> bean) {
return poolScope.get().getPoolKey(bean) != null;
}

<T> void createInstancePool(Contextual<T> contextual, Pooled poolSettings) {
public <T> void createInstancePool(Contextual<T> contextual, Pooled poolSettings) {
instancePools.put(contextual, new InstancePool<>(contextual, poolSettings));
}

Expand Down
Loading

0 comments on commit 0ed842f

Please sign in to comment.