Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8caac67
Pooled annotation like in EJB @Stateless, but for CDI beans
arjantijms Jan 12, 2026
43ad4f8
Add newline
arjantijms Jan 13, 2026
43e1cf1
Update api/src/main/java/jakarta/enterprise/concurrent/Pooled.java
arjantijms Jan 13, 2026
e566893
Update api/src/main/java/jakarta/enterprise/concurrent/Pooled.java
arjantijms Jan 13, 2026
e30f1f8
Update api/src/main/java/jakarta/enterprise/concurrent/Pooled.java
arjantijms Jan 13, 2026
c16730b
Update api/src/main/java/jakarta/enterprise/concurrent/Pooled.java
arjantijms Jan 13, 2026
ee05b78
Update api/src/main/java/jakarta/enterprise/concurrent/Pooled.java
arjantijms Jan 13, 2026
a58dda7
Add exception type to the API for pool timeout
arjantijms Jan 13, 2026
4748e50
Update api/src/main/java/jakarta/enterprise/concurrent/Pooled.java
arjantijms Jan 13, 2026
128621a
Update api/src/main/java/jakarta/enterprise/concurrent/Pooled.java
arjantijms Jan 13, 2026
6659a3b
Update api/src/main/java/jakarta/enterprise/concurrent/Pooled.java
arjantijms Jan 13, 2026
01e5cf4
Update api/src/main/java/jakarta/enterprise/concurrent/Pooled.java
arjantijms Jan 13, 2026
7662842
Update api/src/main/java/jakarta/enterprise/concurrent/Pooled.java
arjantijms Jan 13, 2026
6556b15
Use TimeoutException instead of PoolLockTimeoutException
arjantijms Jan 13, 2026
0786f4c
Change timeout default to seconds instead of minutes
arjantijms Jan 13, 2026
268dbbb
Extended class documentation
arjantijms Jan 14, 2026
3c81743
Checkstyle workaround
arjantijms Jan 14, 2026
cb4724c
Update api/src/main/java/jakarta/enterprise/concurrent/Pooled.java
arjantijms Jan 14, 2026
1b92c64
Update api/src/main/java/jakarta/enterprise/concurrent/Pooled.java
arjantijms Jan 14, 2026
4b2159c
Update api/src/main/java/jakarta/enterprise/concurrent/Pooled.java
arjantijms Jan 14, 2026
9f66a1e
Fix compile error and use JDK non-null
arjantijms Jan 14, 2026
6d51d2e
Alternative for avoiding checkstyle bug
arjantijms Jan 14, 2026
5786e03
Update api/src/main/java/jakarta/enterprise/concurrent/Pooled.java
arjantijms Jan 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2026 Contributors to the Eclipse Foundation.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package jakarta.enterprise.concurrent;

/**
* This exception indicates that an attempt to concurrently access a {@link Pooled}
* bean method resulted in a timeout.
*
* @since 3.2
*/
public class PoolLockTimeoutException extends RuntimeException {

private static final long serialVersionUID = 1L;

/**
* <p>Constructor for PoolLockTimeoutException.</p>
*/
public PoolLockTimeoutException() {
}

/**
* <p>Constructor for PoolLockTimeoutException.</p>
*
* @param message a {@link java.lang.String} object.
*/
public PoolLockTimeoutException(String message) {
super(message);
}
}
258 changes: 258 additions & 0 deletions api/src/main/java/jakarta/enterprise/concurrent/Pooled.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
/*
* Copyright (c) 2026 Contributors to the Eclipse Foundation.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package jakarta.enterprise.concurrent;

import jakarta.enterprise.context.NormalScope;
import jakarta.enterprise.util.AnnotationLiteral;

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

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.MINUTES;

/**
* Specify that a bean is to be pooled.
*
* <p>
* Pooled beans can have multiple instances that are shared between all threads of an application, but can never be
* called by more than one thread at the same time. When a method call on a pooled bean is performed, an instance is
* selected from the pool and is locked until the method call ends. When performing multiple method calls directly after
* each other, multiple different beans may be used.
* </p>
*
* <p>
* Example of usage
*
* {@snippet lang="java" :
* @Pooled
* public class SomeBean {
*
* public void myMethod() {
*
* }
* }
*
* @Inject
* SomeBean someBean;
*
* // myMethod call is directed to a free bean from the pool
* // for the duration of the method call.
* someBean.myMethod();
*
* // myMethod call is again directed to a any free bean from the pool,
* // which may be a totally different instance than the bean used
* // to service the first call.
* someBean.myMethod();
* }
*
*
* @since 3.2
*/
@Inherited
@NormalScope
@Documented
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface Pooled {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add "name" parameter to give the bean a name, which could be used in configuration to specify the values externally (via vendor-specific means, until Jakarta Config is available).


/**
* Value for {@link #accessTimeout} that indicates that the bean instance
* running a bean method must be obtained immediately.
* Otherwise, a {@link PoolLockTimeoutException} is thrown.
*/
long IMMEDIATE = 0;

/**
* Value for {@link #accessTimeout} that indicates to wait as long as necessary
* to obtain the bean instance that permits running a bean method.
*/
long UNLIMITED = -1;

/**
* {@return the maximum number of instances in the pool.}
*/
int value() default 10;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest that default should be some magic constant, e.g. -1, so that implementations can distinguish between the default value and value that's explicitly set.

In case of the default, implementations would be able to change the default in an external configuration.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for the accessTimeout parameter - it should be possible to detect whether the value is the default or explicitly set.


/**
* <p>The maximum amount of time to wait to obtain a bean instance instance
* from the pool to run a single bean method against.
* If the lock cannot be obtained within the specified amount of time, a
* {@link PoolLockTimeoutException} is thrown.</p>
*
* <p>Use the {@link #IMMEDIATE} constant to avoid waiting.</p>
*
* <p>Use the {@link #UNLIMITED} constant to avoid timing out.</p>
*
* <p>The supplied timeout value must be positive or one of the constants
* described above.</p>
*
* @return the time to wait to obtain a bean instance for the duration of
* a single method call.
*/
long accessTimeout() default 5;

/**
* {@return the {@link TimeUnit} to use for the {@link #accessTimeout()}}
*/
TimeUnit unit() default MINUTES;

/**
* The types of {@link Throwable Throwables} which must cause the destruction of a pooled bean instance.
* <p>
* If the pooled bean instances throws any Throwable which is an instance of a type set in the destroyOn property,
* then the bean will be destroyed, except if the throwable is also an instance of a type set in the {@link
* #dontDestroyOn()} property.
* </p>
*
* @return The types of {@link Throwable Throwables} which must cause the destruction of a pooled bean instance
*/
Class<? extends Throwable>[] destroyOn() default {};

/**
* The types of {@link Throwable Throwables} which must not cause the destruction of a pooled bean instance.
* <p>
* If the {@link #destroyOn()} property is empty, but the keepOn property is not, then the bean will be
* destroyed for any Throwable that isn't an instance of a type in the keepOn property.
* When the {@link #destroyOn()} property is not empty, then the dontDestroyOn property takes precedence.
* If a pooled bean instance throws a throwable which is an instance of a type in both the
* destroyOn and keepOn properties, then the bean will not be destroyed.
* </p>
*
* @return The types of {@link Throwable Throwables} which must not cause the destruction of a pooled bean
* instance.
*/
Class<? extends Throwable>[] keepOn() default {};

/**
* Supports inline instantiation of the Pooled annotation.
*
* @since 3.2
*/
public static final class Literal extends AnnotationLiteral<Pooled> implements Pooled {

private static final long serialVersionUID = 1L;

private final int value;
private final long accessTimeout;
private final TimeUnit unit;
private final Class<? extends Throwable>[] destroyOn;
private final Class<? extends Throwable>[] keepOn;

/**
* Default instance of the {@link Pooled} annotation.
*/
@SuppressWarnings("unchecked")
public static final Literal INSTANCE = of(
10,
5,
MINUTES,
new Class[]{},
new Class[]{}
);

/**
* Instance of the {@link Pooled} annotation.
*
* @param value The maximum number of instances in the pool.
* @param accessTimeout The maximum amount of time to wait attempting to retrieve an instance from the pool.
* @param unit The {@link TimeUnit} to use for the {@link #accessTimeout()}
* @param destroyOn The types of {@link Throwable Throwables} which must cause the destruction of a pooled bean instance.
* @param keepOn The types of {@link Throwable Throwables} which must not cause the destruction of a pooled bean instance.

* @return instance of the {@link Pooled} annotation
*/
public static Literal of(
final int value,
final long accessTimeout,
final TimeUnit unit,
final Class<? extends Throwable>[] destroyOn,
final Class<? extends Throwable>[] keepOn


) {
return new Literal(
value,
accessTimeout,
unit,
destroyOn,
keepOn
);
}

private Literal(
final int value,
final long accessTimeout,
final TimeUnit unit,
final Class<? extends Throwable>[] destroyOn,
final Class<? extends Throwable>[] keepOn
) {

this.value = value;
this.accessTimeout = accessTimeout;
this.unit = unit;
this.destroyOn = destroyOn;
this.keepOn = keepOn;
}


/**
* {@return the maximum number of instances in the pool.}
*/
@Override
public int value() {
return value;
}

/**
* {@return the maximum amount of time to wait attempting to retrieve an instance from the pool.}
*/
@Override
public long accessTimeout() {
return accessTimeout;
}

/**
* {@return the {@link TimeUnit} to use for the {@link #accessTimeout()}}
*/
@Override
public TimeUnit unit() {
return unit;
}

/**
* {@return the types of {@link Throwable Throwables} which must cause the destruction of a pooled bean instance.}
*/
@Override
public Class<? extends Throwable>[] destroyOn() {
return destroyOn;
}

/**
* {@return the types of {@link Throwable Throwables} which must not cause the destruction of a pooled bean instance.}
*/
@Override
public Class<? extends Throwable>[] keepOn() {
return keepOn;
}

}
}
Loading