views:

3907

answers:

3

I have two systems I'm trying to integrate. One is built on raw servlets, the new one is build on JSF with IceFaces. I'm trying to facilitate cross-system sign on. The idea is that I have a button in the old system that POSTs the appropriate information to the new site and logs them on.

Well, ideally, I'd like to use just a regular old servlet to facilitate that on the new site. Go to the new site's Servlet, do what it needs to do and the forward onto the dashboard.

Our security is handled via a managed bean. However, by the time you get to the Servlet, there is no faces context. So, how would I create a new faces context?

I have a back up plan in that I can always link to a dummy .iface page which will create the FacesContext for me and then create a backing bean that will do stuff when it gets instanciated and then forward onto the main page. But this feels very much like a hack.

Any help would be appreciated!

EDIT: I went with the back up way. Basically, I POST to a page like so:

<f:view>
   <ice:outputText value="#{EntryPoint}"/>
</f:view

The backing bean looking like so...

public EntryPoint() {
      try {
         HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
         HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse(); 
         String loginID = request.getParameter("loginID");
         //Do some code to load the user/permissions
         response.sendRedirect(
            //The appropriate page
         );
      } catch (IOException ex) {
         logger.error(null, ex);
      } catch (SQLException ex) {
         logger.error(null, ex);
      }
   }

This still feels like a hack, but I'm not sure how to get around this. Ideally, I'd POST to a servlet, get the loginID, build the user and put it directly into the managed bean. But, the FacesContext does not exist at that point.

Any other ideas?

+2  A: 

I'm not sure what you mean by "site" in this context.

A couple of notes:

  • Managed beans will be never be available outside the web app (WAR) in which they are defined.
  • FacesContext object instances are ultimately created and disposed by FacesServlet.service, so requests should pass through this servlet. Attempting to create a context under other circumstances might lead to undefined behaviour.


Bearing that in mind, you could create a request sequence like this:

FacesServlet (mapping: /faces/*)
 -> /faces/jsfPage.jsp (a JSP with JSF controls)
    -> DispatchBean (calls ExternalContext.dispatch("/AnotherServlet")
       -> AnotherServlet

jsfPage.jsp contains:

<f:view>
 <h:outputText value="#{dispatchBean.dispatch}" />
</f:view>

The "dispatch" property resolves to a bean method "getDispatch":

public String getDispatch() {
 FacesContext context = FacesContext.getCurrentInstance();
 try {
  context.getExternalContext().dispatch("/FacesClientServlet");
 } catch (IOException e) {
  throw new FacesException(e);
 }
 return null;
}

Which dispatches to this servlet:

public class FacesClientServlet extends javax.servlet.http.HttpServlet
     implements javax.servlet.Servlet {

    static final long serialVersionUID = 1L;

    @Override
    protected void doGet(HttpServletRequest request,
      HttpServletResponse response) throws ServletException, IOException {

     FacesContext context = FacesContext.getCurrentInstance();
     ELContext elContext = context.getELContext();
     ExpressionFactory expressionFactory = context.getApplication()
       .getExpressionFactory();
     ValueExpression expression = expressionFactory.createValueExpression(
       elContext, "#{myBean.text}", Object.class);
     Object value = expression.getValue(elContext);

     ResponseWriter writer = context.getResponseWriter();
     writer.write("" + value);

    }

}

Which emits the value from a managed bean "myBean":

public class MyBean {

    private final String text = "Hello, World!";

    public String getText() {
     return text;
    }

}

This is all very convoluted and I wouldn't willingly do any of it.


An alternative, which may come with its own consequences, is to create your own context like this:

public class ContextServlet extends javax.servlet.http.HttpServlet implements
     javax.servlet.Servlet {
    static final long serialVersionUID = 1L;

    private FacesContextFactory facesContextFactory;
    private Lifecycle lifecycle;

    @Override
    public void init(ServletConfig config) throws ServletException {
     super.init(config);

     LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder
       .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
     facesContextFactory = (FacesContextFactory) FactoryFinder
       .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
     lifecycle = lifecycleFactory
       .getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
    }

    @Override
    protected void doGet(HttpServletRequest request,
      HttpServletResponse response) throws ServletException, IOException {

     FacesContext context = facesContextFactory.getFacesContext(
       getServletContext(), request, response, lifecycle);
     try {
      ELContext elContext = context.getELContext();
      ExpressionFactory expressionFactory = context.getApplication()
        .getExpressionFactory();
      ValueExpression expression = expressionFactory
        .createValueExpression(elContext, "#{myBean.text}",
          Object.class);
      Object value = expression.getValue(elContext);

      PrintWriter pw = response.getWriter();
      try {
       pw.write("" + value);
      } finally {
       pw.close();
      }
     } finally {
      context.release();
     }
    }

}

Again, I would avoid this approach if possible.

McDowell
The second is the answer I was looking for and the first was my back up plan if I couldn't figure out the first.The idea was that the legacy Servlet site (its own webapp and WAR file) would redirect the user with a POST and credentials to the new webapp. Is there a better way to do this?
Drew
A: 

Did you ever find a better solution? I have the same problem ...

There must be a better way! :(

I'd really appreciate any tiny hint!

Felix
Please ask a question by pressing `ask question` button at right top, instead of posting it as an answer. You should in your new question also elaborate *what exactly* you need the FacesContext for at end. E.g. just for the ExternalContext#getSessionMap() or so.
BalusC
A: 

Hello,

I am experiencing the same issue.

In fact I have to render components without any session active, so without FacesContext instance.

The problem is that the encodeBegin and encodeEnd methods need a FacesContext instance, so I have to create manually one..

I would prefer another solution.

Guillaume

Guillaume
Welcome at Stackoverflow! Please don't post questions as answers, they will get lost in noise. Press the `Ask Question` button at top to post a question, not the `Post Your Answer` button at the bottom ;)
BalusC