JAX-RS 2& Jersey(2.10):并不总是注入我的SessionScoped Bean

时间:2014-09-12 05:13:26

标签: rest java-ee glassfish cdi jersey-2.0

过去两天我在网上搜索,我仍然找不到解决我这个奇怪问题的方法。

我有一个@SessionScoped bean,一个带有@Stateless注释的Restful Jersey服务和@Inject the bean以及@EJB my ejb。

当我尝试从浏览器的URL调用其余的Web服务时,该服务会注入我的@Inject userBean。 但是当我尝试从@SessionScoped UserBean中的代码调用Web服务时,它没有注入。

The Bean:

@SessionScoped
@Named("userBean")
public class UserBean implements Serializable {

@EJB
private UserEJB userEJB;

private User user = new User();

public User getUser() {
    return user;
}

public void setUser(User user) {
    this.user = user;
}

private String newStatusContent;

public String getNewStatusContent() {
    return newStatusContent;
}

public void setNewStatusContent(String content) {
    this.newStatusContent = content;
}

public void publishStatus() {
    if (newStatusContent != null && newStatusContent.trim().isEmpty() == false) {
        //Maybe a way to set manualy the user property of this bean here?
        Client client = ClientBuilder.newClient();
        Response response = client.target("http://localhost:8080/services/posts/status")
                .queryParam("content", newStatusContent)
                .request(MediaType.APPLICATION_JSON).get();
        newStatusContent = "";
        FacesContext context = FacesContext.getCurrentInstance();
        HttpServletRequest request
                = (HttpServletRequest) context.getExternalContext().getRequest();
        if (request.getRequestURI().equals("/index/profile/profile.xhtml") &&     request.getParameter("prettyname") != null && request.getParameter("prettyname").equals(user.getPrettyname())) {
        }
        if (response.getStatus() == 200) {
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Δημοσίευση", "Η νέα κατάσταση δημοσιεύτηκε επιτυχώς."));

        } else {
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Δημοσίευση", "Συγγνώμη, κάτι πήγε στραβά με την νέα κατάσταση :("));

        }

    }

}

 public String login() throws ServletException {
    User authenticatedUser;
    if (userEJB.mailExists(user)) {
        if ((authenticatedUser = userEJB.authenticate(user)) != null) {
            user = authenticatedUser;
            //and store the api key for rest api
            // userEJB.storeApiKey(user.getUserId());
        } else {
            FacesContext.getCurrentInstance().addMessage("mail", new FacesMessage("Wrong password for this e-mail"));
            return "";
        }

    } else {
        user = userEJB.create(user);
    }

    if (getPrevURI().isEmpty() == false) {
        return getPrevURI();
    } else {
        return "/index/index.xhtml?faces-redirect=true";
    }

}
} 

RestApplication类(没有web.xml):

@ApplicationPath("/services/*")
public class RestApplication extends javax.ws.rs.core.Application{

@Override
public Set<Class<?>> getClasses() {
return new HashSet<Class<?>>(Arrays.asList(
        UserService.class, 
        MessageService.class, PostService.class));
}

}

Restful Webservice类:

@Path("/posts")
@Stateless
public class PostService {

@EJB
private PostEJB postEJB;

@Inject
UserBean userBean;

@Path("/status")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response publishStatus(@QueryParam("content") String content) {
    if (content.isEmpty() || userBean.getUser().getUserId() == null) {
        System.out.println("null user id or content but?" + content);
        return Response.status(Response.Status.BAD_REQUEST).build();
    }
    Post postStatus = new Post();
    postStatus.setContent(content.trim());
    postStatus.setPublisher(userBean.getUser());
    postStatus.setPublisherUserId(userBean.getUser().getUserId());
    postEJB.create(postStatus);
    System.out.println("post id : "+postStatus.getPostId());
    PostStatus status = new PostStatus(postStatus.getPostId());
    status.setReceiver(userBean.getUser());
    status.setReceiverId(userBean.getUser().getUserId());
    postEJB.postStatus(status);
    return Response.status(Response.Status.OK).build();
}

//*this is not the problem I tried to remove and still doesn't worked*
@PreDestroy
public void destruct() {
    postEJB.destruct();

  }
}

Beans.xml(bean-discovery-mode to&#39; all&#39;)

 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee  http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
    bean-discovery-mode="all"
    >

 </beans>

我的步骤:

  • 1:从我的login.xhtml页面登录,
  • 2:当我尝试时:
    http://localhost:8080/services/posts/status?content=Test status
  • 3:检查数据库和状态是否存在
  • 4:当我尝试从UserBean(publishStatus方法)调用相同的服务时,
  • 5:输出为:&#39; null用户ID或内容但是?测试状态2&#39;所以内容发送成功但UserBean的getUser()。getUserId()为null
  • 6:检查数据库,新状态不存在“

我使用的是Glassfish 4.1,JDK 8.20,Jersey 2.10(模块玻璃鱼) 我做错了什么?为什么要使用浏览器的网址而不是来自代码的客户端来电?

-----使用&#39; API KEY&#39;工作替代方式-----------
保存加密Cookie(用于浏览器&#39; s / js调用)和On Encrypted Session

我发现进行安全身份验证的另一种方式,但这不是我想要的:

ApiAuthenticator服务过滤器:

public class ApiAuthenticator implements ClientRequestFilter {

private static final StandardPBEStringEncryptor encryptor;

static {
    encryptor = new StandardPBEStringEncryptor();
    encryptor.setPassword("jasypt");
}

public static String encrypt(String theString) {
    return encryptor.encrypt(theString);
}

public static String decrypt(String encryptedString) {
    return encryptor.decrypt(encryptedString);
}
private final String apikey;

public ApiAuthenticator(String apikey) {
    this.apikey = apikey;
}

@Override
public void filter(ClientRequestContext requestContext) throws IOException {
    //requestContext.getCookies().putIfAbsent("apikey", new Cookie("apikey", apikey));
    requestContext.getHeaders().add("apikey", encrypt(apikey));

}

}

在PostService中编辑了publishStatus方法:

@Path("/status")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response publishStatus(@QueryParam("content") String content, @Context HttpHeaders headers)   {

    String apikey = headers.getHeaderString("apikey");
    if (apikey == null) {
        apikey = headers.getCookies().get("apikey").getValue();
    }

    if (content.isEmpty() || apikey == null) {
        return Response.status(Response.Status.BAD_REQUEST).build();
    }
    User user = userEJB.findByApiKey(ApiAuthenticator.decrypt(apikey));
    Post postStatus = new Post();
    postStatus.setContent(content.trim());
    postStatus.setPublisher(user);
    postStatus.setPublisherUserId(user.getUserId());
    postEJB.create(postStatus);
    PostStatus status = new PostStatus(postStatus.getPostId());
    status.setReceiver(user);
    status.setReceiverId(user.getUserId());
    postEJB.postStatus(status);
    return Response.status(Response.Status.OK).build();
}

编辑从bean到服务的客户端调用:

 Client client = ClientBuilder.newClient();

        Response response = client.target("http://localhost:8080/services/posts/status")
                .queryParam("content", newStatusContent)
                .register(new ApiAuthenticator(apiEJB.getApiKey(user.getUserId()).getApiKey()))
                .request(MediaType.APPLICATION_JSON).get();

用户成功登录后,在UserBean上编辑了登录方法:

   ((HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse())
                    .addCookie(new Cookie("apikey",   ApiAuthenticator.encrypt(apiEJB.storeApiKey(user.getUserId()).getApiKey())));

RestApplication.java:

@ApplicationPath("/services/*")
public class RestApplication extends javax.ws.rs.core.Application{

@Override
public Set<Class<?>> getClasses() {
return new HashSet<Class<?>>(Arrays.asList(
        ApiAuthenticator.class ,
        UserService.class, 
        MessageService.class, PostService.class));

}


}

解决方法是@Inject在UserBean中的PostService

上的

@Inject PostService postService;

在UserBean.login方法上,删除所有其余客户端代码并替换为:

postService.publishStatus(newStatusContent);

但是,

引起:org.jboss.weld.context.ContextNotActiveException:WELD-001303:范围类型javax.enterprise.context.SessionScoped没有活动的上下文当我尝试在WebSocket ServerEndPoint中注入MessageService时

@ServerEndpoint(value = "/websocket", encoders = {MessageEncoder.class},
    decoders = {MessageDecoder.class}, configurator = GetHttpSessionConfigurator.class)

public class ChatServerEndPoint {

@PersistenceContext(unitName = "CosmosDBPeristenceUnit")
private EntityManager em;

private static Set<Session> peers = Collections.synchronizedSet(new HashSet<Session>());

@Inject
private MessageService messageService;
//private Session session;
private int thisUserId;
private User thisUser;

@OnOpen
public void onOpen(Session thisSession, EndpointConfig config) throws IOException, EncodeException {
    thisUserId = (int) config.getUserProperties().get("userId");
    thisUser = em.find(User.class, thisUserId);
    thisSession.getUserProperties().put("userId", thisUserId);

    Iterator<Session> it = peers.iterator();
    while (it.hasNext()) {
        Session session = it.next();
        int userId = (int) session.getUserProperties().get("userId");

        thisSession.getBasicRemote().sendObject(new UserStatusMessage(em.find(User.class, userId), true));
        session.getBasicRemote().sendObject(new UserStatusMessage(thisUser, true));
    }

    peers.add(thisSession);
}

/* we don't want @stateless problems with chat info except message, I will do it with rest public   */
@OnMessage
public void onMessage(WebsocketMessage message) {
    try {
        Iterator<Session> it = peers.iterator();
        if (message instanceof ChatMessage) {
            ChatMessage msg = (ChatMessage) message;
            msg.setSender(thisUser);
            boolean otherUserIsOnline = false;
            while (it.hasNext()) {
                Session receiver = it.next();
                if ((int) receiver.getUserProperties().get("userId") == msg.getReceiver_userId()) {
                    receiver.getBasicRemote().sendObject(msg);
                    otherUserIsOnline = true;
                    break;
                }
            }
            if (otherUserIsOnline == false) {
                //save the message to database via rest
                messageService.addMessage(msg.getReceiver_userId(), msg.getMessage());
            }
        } else {
            while (it.hasNext()) {
                Session receiver = it.next();
                receiver.getBasicRemote().sendObject(message);
            }
        }

    } catch (IOException | EncodeException ex) {
        System.out.println("Exception on endpoint: " + ex.getMessage());
    }
}

@OnClose
public void onClose(Session session) throws IOException, EncodeException {
    peers.remove(session);
    Iterator<Session> it = peers.iterator();
    while (it.hasNext()) {
        Session otherSession = it.next();    
        otherSession.getBasicRemote().sendObject(new UserStatusMessage(thisUser, false));

    }

}

@OnError
public void onError(Throwable t) {
    t.printStackTrace();
}

}

GetHttpSessionConfigurator.class:

public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator {

@Override
public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
    HttpSession ses = (HttpSession) request.getHttpSession();
    config.getUserProperties().put("userId", ses.getAttribute("userId"));
}
}

MessageService.java:

@Stateless
@Path("/messages")
public class MessageService {

@EJB
UserEJB userEJB;

@EJB
ConversationEJB convEJB;

@Inject
UserBean userBean;


@Path("/{otherUserId}")
@GET
@Produces("application/json; charset=UTF-8")
public List<Message> findConversation(@PathParam("otherUserId") int otherUserId) {
    User otherUser = userEJB.findById(otherUserId);

    return convEJB.findConversation(userBean.getUser(), otherUser);

}


@Path("/{receiverUserId}")
@POST
@Produces("application/json; charset=UTF-8")
public Message addMessage(@PathParam("receiverUserId") int receiverUserId, @QueryParam("body") String body) {
    return convEJB.addMessage(userBean.getUser(), userEJB.findById(receiverUserId), body);
}

@Path("/{messageId}")
@DELETE
public void removeMessage(@PathParam("messageId") int messageId) {
    convEJB.removeMessage(messageId);
}

@Path("/{userId}/{otherUserId}")
@DELETE
public void clearConversation(@PathParam("userId") int userId, @PathParam("otherUserId") int otherUserId) {
    convEJB.clearConversation(userEJB.findById(userId), userEJB.findById(otherUserId));
}

}

提前致谢!

1 个答案:

答案 0 :(得分:0)

由于我不知道你的应用程序的确切流程,这可能是错误的,但这是我的假设:
您的userBean是会话范围的,它保存用户数据。在你的应用程序的某种程度上,它可能已初始化。

因此,一旦您从浏览器调用您的服务,它就会发送会话cookie以正确定位会话数据。

但是当你从代码中调用服务时,它没有传递那个会话cookie,所以在服务器端它创建了全新的会话,因此新的userBean带有空的用户数据。这就是为什么你有UserId null的原因。 Bean本身是注入的,否则你将在这里有空指针:

userBean.getUser().getUserId() // as userBean would be null

所以要在该调用中找到正确的用户bean,您需要将当前的会话cookie添加到该调用中。

PS。 但是为什么你要通过http向bean发出请求?注入服务bean并调用其方法而不是进行http调用会不会更合乎逻辑?