Java 如何避免ExecutorService重写Runnable的安全主体

Java 如何避免ExecutorService重写Runnable的安全主体,java,multithreading,jakarta-ee,concurrency,cdi,Java,Multithreading,Jakarta Ee,Concurrency,Cdi,当提交的第一个runnable是InjectExecutorService时,将为该runnable正确设置安全主体。随后提交的每个runnable都会获得原始用户的安全主体,而不是保持当前runnable。我的开发机器运行的是Wildfly 8.2 我正在为异步处理创建一个报告系统。我创建了一个服务,用于检查创建任务的用户,并确保只有该用户才能启动或完成任务。服务代码如下所示 @Stateless public class ReportingService { //EE injectio

当提交的第一个runnable是InjectExecutorService时,将为该runnable正确设置安全主体。随后提交的每个runnable都会获得原始用户的安全主体,而不是保持当前runnable。我的开发机器运行的是Wildfly 8.2

我正在为异步处理创建一个报告系统。我创建了一个服务,用于检查创建任务的用户,并确保只有该用户才能启动或完成任务。服务代码如下所示

@Stateless
public class ReportingService {
   //EE injection security context
   @Resource
   SessionContext context;
   //CDI security Principal
   @Inject
   Principal principal;

   //this method handles getting the username for EE injection or CDI
   private String getCurrentUser() {
       if (context != null) {
           return context.getCallerPrincipal().getName();
       }
       if (principal != null) {
           return principal.getName();
       }
       return null;
   }

   @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
   @Transactional
   public void registerTask(String taskId) {
       //Create task
       //set task.submittedBy = getCurrentUser()
       //persist task
       //code has been omitted since it is working
   }

   private void validateCurrentUserRegisteredJob(String taskId) {
       String user = //get user that created task with id = id from DB
       String currentUser = getCurrentUser();
       if (!user.equals(currentUser)) {
           throw new EJBAccesException("Current user "+currentUser+" did not register task");
       }
   }

   @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
   @Transactional
   public void startTask(String taskId) {
       validateCurrentUserRegisteredJob(taskid);
       //retrieve task entity, set start time to now, and merge
   }
   ...
}
下面是我的可运行代码

public TaskRunner() implements Runnable {
    //CDI principal
    @Inject
    Principal principal;
    @Inject
    ReportingService rs;

    private taskId;

    public void setTaskId() {...}

    public void run() {
       log.debug("Inside Runner Current User: "+principal.getName());
       rs.startTask(taskId);
       ....
    }
}
以下是启动进程的REST端点调用的无状态Bean的代码

@Stateless
public ProjectService() {
    @Inject
    Instance<TaskRunner> taskRunner;
    @Inject
    ReportingService reportingService;

    //ExecutorService that is create from Adam Bien's porcupine project
    @Inject
    @Dedicated
    ExecutorService es;

    //method that is called by rest enpoint to kick off 
    public void performAsynchAction(List<String> taskIds, ...rest of args...) {
        taskIds.stream().forEach(t -> {
            //registers task with user that made REST call
            reportingService.registerTask(t);
            TaskRunner runner = taskRunner.get();
            runner.setTaskId(t);
            log.debug("Created runner. Principal: "+runner.principal.getName());
            es.submit(runner);
        });
    }
}
我第一次将Rest端点作为user1调用,并注册tasks:1-2。它们都按预期工作,我在日志中得到以下输出

Created runner. Principal: user1
Created runner. Principal: user1
Inside Runner Current User: user1
Inside Runner Current User: user1
下次我与user2进行相同的REST调用时,我在日志中得到以下输出

Created runner. Principal: user2
Inside Runner Current User: user1
EJBAccessException Current user user1 did not register task
第一次向ExecutorService提交Runnable时,Runnable的安全主体似乎设置正确。但是对于提交给ExecutorService的每个后续Runneable,它都使用第一个提交的runnable的安全主体。这是一个bug还是预期的行为?有人知道可能的工作吗

编辑:我发现我用来创建ExecutorService的豪猪项目没有被容器管理。当我切换到ManagedExecutorService时,SessionContext被正确地传播

@Resource(lookup = "java:jboss/ee/concurrency/executor/customExecutor")
private ManagedExecutorService es;

我认为问题在于,您
@将
@依赖
作用域
执行服务
注入
@无状态
bean。
@无状态
bean可以合并和重用,而
@依赖
CDI bean通过引用存储,因此在重用
@无状态
bean时不会重新创建

在不知道
ExecutorService
的实现的情况下,我猜它会在第一次运行时创建上下文线程,并在第二次运行时在不调整上下文的情况下重用它们

您可以“强制”您的
ProjectService
创建一个新的ExecutorService,方法是将其封装到
@RequestScoped
bean中:

@RequestScoped
public class ESHolder {
    @Inject
    @Dedicated
    ExecutorService eS;

    public ExecutorService getES() {
        return eS;
    }
}


@Stateless
public ProjectService() {
    // ...

    @Inject        
    ESHolder esHolder;

    public void performAsynchAction(List<String> taskIds, ...rest of args...) {
        taskIds.stream().forEach(t -> {
            // ...

            esHolder.getES().submit(runner); 
        });
    }
}
@RequestScoped
公共类ESHolder{
@注入
@专注的
遗嘱执行人;
公共服务getES(){
返回eS;
}
}
@无国籍
公共工程服务(){
// ...
@注入
埃舍尔德;
public void performAsynchAction(列出任务ID,…其他参数…){
taskIds.stream().forEach(t->{
// ...
esHolder.getES().submit(runner);
});
}
}

我解决了这个问题。我查看了豪猪代码,发现ExecutorService不是由容器管理的。我创建了一个ManagerExecutorService,然后SessionContext被正确地传播

@Resource(lookup = "java:jboss/ee/concurrency/executor/customExecutor")
private ManagedExecutorService es_;

你知道你的当前用户是从哪里来的吗
SessionContext
Principal
?事实证明,它们都被注入,并且在第二次调用的Runnable和service中都有不正确的值。我尝试了这个方法,但我仍然在注入的SessionContext和Principal中得到不正确的主体
@Resource(lookup = "java:jboss/ee/concurrency/executor/customExecutor")
private ManagedExecutorService es_;