首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java EE侦听器中止响应

Java EE侦听器中止响应
EN

Stack Overflow用户
提问于 2018-01-01 20:51:24
回答 2查看 541关注 0票数 0

我尝试创建一个多租户REST API。

Java EE 7

应用-服务器: WildFly-Swarm 2017.11.0

JAX-RS: wildfly-swarm-weld

其目的是从查询参数中获取租户(德语Mandant)名称,并设置租户的名称以触发代理EntityManager。

这是TOMAS DVORAK的基本概念:https://www.tomas-dvorak.cz/posts/jpa-multitenancy/

我很难使用拦截器,正如标题中所说,我需要用HTTP代码和JSON错误消息中止被拦截的REST请求的响应。

我不能使用筛选器,因为EE正在使用另一个带有筛选器的线程,并且我无法通过ThreadLocal传递租户名称。

我很难获得响应对象。

以下是我到目前为止编写的代码:

代码语言:javascript
复制
import javax.inject.Inject;
import javax.interceptor.*;
import javax.servlet.http.*;
import org.multitenancy.test.beans.*;

/**
 * Wrap every call with tenant identification, detected from list of parameters
 * of called method.
 */
@Interceptor
public class TenantInterceptor
{

  @Inject
  private TenantRegistry tenantRegistry;

  @Inject
  HttpServletRequest servletRequest;

  @AroundInvoke
  public Object wrapWithTenant(final InvocationContext ctx) throws Exception
  {
    System.out.println("wrapWithTenant() Called");
    printParameter();
    if (servletRequest.getParameterMap().containsKey("mandant"))
    {
      String mandantNameReq = servletRequest.getParameterMap().get("mandant")[0];
      if (tenantRegistry.verifyMandantByName(mandantNameReq))
      {
        System.out.println(mandantNameReq + " is verified");
        final String oldValue = TenantHolder.getCurrentTenant();
        System.out.println("old value " + oldValue);
        try
        {
          TenantHolder.setTenant(mandantNameReq);
          System.out.println("Mandant gesetzt: " + mandantNameReq);
          return ctx.proceed();
        }
        finally
        {
          if (oldValue != null)
          {
            TenantHolder.setTenant(oldValue);
          }
          else
          {
            TenantHolder.cleanupTenant();
          }
        }
      }
      else
      {
        //TODO: Response einbauen
//        containerRequestContext.abortWith(
//                Response.status(Response.Status.BAD_REQUEST)
//                        .entity(new ApiError("Mandant not found"))
//                        .type(MediaType.APPLICATION_JSON)
//                        .build());
      }
    }
    else
    {
//      containerRequestContext.abortWith(
//              Response.status(Response.Status.BAD_REQUEST)
//                      .entity(new ApiError("Parameter doesnt contain mandant"))
//                      .type(MediaType.APPLICATION_JSON)
//                      .build());
    }
    return null;
  }

  private void printParameter()
  {
    if (servletRequest.getParameterMap().isEmpty())
    {
      System.out.println("No Properties given");
    }
    else
    {
      for (String key : servletRequest.getParameterMap().keySet())
      {
        for (String val : servletRequest.getParameterValues(key))
        {
          System.out.println(key + "\t" + val);
        }
      }
    }
  }
}

提前感谢:)

EN

回答 2

Stack Overflow用户

发布于 2018-01-03 01:59:13

我会避免将内容存储在线程局部变量中,因为这取决于容器实现是否使用相同的线程进行请求分派。它可以在一个容器中工作,但不能在另一个容器中工作。

我会做以下事情:

1)为租户创建一个可注入的bean定义:

代码语言:javascript
复制
@javax.enterprise.inject.Produces
Tentant tenant(HttpServletRequest request) throws TenantNotFoundException {
  // logic
}

2) JAX-RS中的异常处理

代码语言:javascript
复制
@javax.ws.rs.ext.Provider
public class TenantNotFoundExceptionMapper implements javax.ws.rs.ext.ExceptionMapper<TenantExceptionNotFound> {
  public Response toResponse(TenantNotFoundException exception) {
    // your 400 response here
  }
}

3)将租户注入您的业务逻辑

代码语言:javascript
复制
@Path("/foo")
public class Foo {
  @javax.inject.Inject
  private Tenant tenant
}
票数 1
EN

Stack Overflow用户

发布于 2018-01-11 22:46:45

我无法使用筛选器,因为EE正在使用另一个带有筛选器的线程,并且我无法通过ThreadLocal传递租户名称。

为何会这样呢?您如何访问该应用程序?如果通过REST访问它,这是应用程序的入口点,从此就可以访问ThreadLocal了。重要的是,您将@Priority设置为一个较低的值,以便过滤器成为最先执行的过滤器之一。

我们在应用程序中使用了类似的方法,将租户标识符保存在ThreadLocal

代码语言:javascript
复制
@Provider
@Priority(1)
public class TenantFilter implements ContainerRequestFilter
{
    private static final Logger LOG = LoggerFactory.getLogger(TenantFilter.class);
    private static final String TENANT_IDENTIFIER = "mandant";

    @Inject
    private TenantRegistry tenantRegistry;        

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException
    {
        List<String> identifierHeader = requestContext.getHeaders().get(TENANT_IDENTIFIER);
        if ((identifierHeader == null || identifierHeader.isEmpty()))
        {
            LOG.error("No header " + TENANT_IDENTIFIER + " found. Access denied. [path: " + path + "]");
            accessForbidden(requestContext);
            return;
        }

        String tenantIdentifier = identifierHeader.get(0);
        if (tenantRegistry.verifyMandantByName(tenantIdentifier)) {
            LOG.trace("Filtering request for " + TENANT_IDENTIFIER + " header, using: " + tenantIdentifier);
            TenantIdentifierResolver.tenantIdentifier.set(tenantIdentifier);
        }
    }

    private void accessForbidden(ContainerRequestContext requestContext)
    {
        Response accessForbidden = Response.status(Status.FORBIDDEN).build();
        requestContext.abortWith(accessForbidden);
    }
}

TenantIdentifierResolver是一个保存tenand标识符的线程本地,Hibernate使用它为数据库访问设置正确的模式访问。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48049799

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档