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?
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.
Related
I have Servlet which add users in database. Servlet use instance of module for work with database he's alias DBJoint. And Servlet get instance of DBJoint from ServletContext.
#WebListener
public class ContextListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
final ServletContext servletContext =
servletContextEvent.getServletContext();
final DBJoint joint = new DBJointHandler(
"database_scripts",
"authentication_database");
servletContext.setAttribute("db", joint);
}
}
And in each Servlet when I need work with database I call ServletContext and get DBJoint by key "db".
public class AddUserServlet extends HttpServlet {
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF8");
try {
final boolean success = addUserInDatabase(req);
if (success) req.setAttribute("serverAnswer", EDIT_SUCCESS.get());
else req.setAttribute("serverAnswer", ERR_UNIQUE_L_P.get());
req.getRequestDispatcher(ANSWER.get())
.forward(req, resp);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* Addition user in database.
*
* #return true if addition success, else false.
*/
private boolean addUserInDatabase(final HttpServletRequest req)
throws SQLException {
final User user = getUserFromRequest(req);
return getDatabaseExecutor().addUserAndGetSuccess(user);
}
/**
* Extracts user's data from HttpServletRequest.
*
* #return user from request.
*/
private User getUserFromRequest(final HttpServletRequest req) {
return new User(
req.getParameter("name"),
req.getParameter("login"),
req.getParameter("password"),
req.getParameter("email"),
req.getParameter("role")
);
}
/**
* Get executor database requests.
*/
private ScriptExecutor getDatabaseExecutor() throws SQLException {
final DBJoint db = (DBJoint) getServletContext().getAttribute("db");
return db.getDBScriptExecutor();
}
}
I need test my Servlet, and use mock object instead original object:
In this test I try verify what body doPost call addUser(User u) in executor of database script. But get NPE.
#Test
public void whenUserAddThenAddUserCall() throws ServletException, IOException, SQLException {
final AddUserServlet servlet = new AddUserServlet();
//mock http.
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
User user = new User("name", "login", "password", "email");
//mock database.
ScriptExecutor executor = mock(ScriptExecutor.class);
DBJoint joint = mock(DBJointHandler.class);
when(joint.getDBScriptExecutor()).thenReturn(executor);
final ServletContext context = request.getServletContext();
//In this place I get NPE but why?
context.setAttribute("db", joint);
servlet.doPost(request, response);
verify(executor).addUser(user);
}
Why I have NullPointerException? How test this class? Thank You!
you need to include these as well
ServletContext context = mock(ServletContext .class)
when(request.getServletContext()).thenReturn(context );
You are calling request.getServletContext() on the request object which is a mock.
Mocked objects do no provide real method implementations. Instead of the real getServletContext() implementation, a stubbed version of the method is called returning null.
A mock should be told what it needs to return when a method is called on it, you need to define behavior just like you did for the DBJoinHandler mock.
To solve the NPE, do something like this:
when(request.getServletContext()).thenReturn(context);
where context could be a ServletContext mock.
this is my first time that I work on a J2EE project and I have the following problem:
I have the following servlet that implement the HttpServlet interface.
public class Salwf extends HttpServlet {
private String msg = StringUtils.EMPTY;
public void init(ServletConfig config) throws ServletException {
super.init(config);
StandardConfigurationFactory standardConfigurationFactory = new StandardConfigurationFactory();
try {
standardConfigurationFactory.init();
} catch (ConfigurationException e) {
throw new ServletException(e);
}
ConfigurationFactory.setInstance(standardConfigurationFactory);
}
public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
LoggerMDC.setup(req, res);
Logger logger = (Logger) Logger.getStdLogger(Monitoraggio.class); // do not declare 'logger' as static field in order to work with MDC
String service = req.getParameter("serv");
char serviceId = Utility.getServizio(req.getParameter("serv"));
// The collection that have to be shown in a table inside the JSP view:
SalDettaglio[] salDettaglio = this.getSalDettaglioList();
gotoPage(ConfigurationFactory.getPropertiesPages().getProperty("pagina_salwf"), req, res);
//String paginaDaLanciare = lanciaServizio(serviceId, req, res);
}
/*
private String lanciaServizio(char num_servizio, HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
Logger logger = (Logger) Logger.getStdLogger(Monitoraggio.class); // do not declare 'logger' as static field in order to work with MDC
HttpSession session;
}
*/
private void gotoPage(String address, HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
Logger logger = (Logger) Logger.getStdLogger(Monitoraggio.class); // do not declare 'logger' as static field in order to work with MDC
logger.debug("gotoPage() | address: " + address);
ServletContext ctx = getServletConfig().getServletContext();
RequestDispatcher dispatcher = ctx.getRequestDispatcher(address);
dispatcher.forward(req, res);
}
private SalDettaglio[] getSalDettaglioList(){
SalDettaglio[] salDettaglioArrayResult;
List<SalDettaglio> salDettaglioList = new ArrayList<SalDettaglio>();
List<RM> rmList = new ArrayList<RM>();
RM rm1 = new RM("codiceRM1", "Andrea", "01/01/1014", "acqRiserva1", "consegnaFinale1", "descrizioneRM1", BigDecimal.valueOf(1000));
RM rm2 = new RM("codiceRM2", "Andrea", "01/01/1014", "acqRiserva2", "consegnaFinale2", "descrizioneRM2", BigDecimal.valueOf(1000));
rmList.add(rm1);
rmList.add(rm2);
RM[] rmArray = (RM[]) rmList.toArray();
SalDettaglio salDettaglio1 = new SalDettaglio("codice1", "stato1", "01/01/2014", "01/06/2014", "Andrea",
"Andrea", "Andrea", BigDecimal.valueOf(1000), "fornitore1",
"rmConRiserva1", "errore1", null, rmArray);
SalDettaglio salDettaglio2 = new SalDettaglio("codice2", "stato2", "01/01/2014", "01/06/2014", "Andrea",
"Andrea", "Andrea", BigDecimal.valueOf(1000), "fornitore2",
"rmConRiserva2", "errore2", null, rmArray);
salDettaglioList.add(salDettaglio1);
salDettaglioList.add(salDettaglio2);
salDettaglioArrayResult = (SalDettaglio[]) salDettaglioList.toArray();
return salDettaglioArrayResult;
}
}
As you can see into the service() method I have this array definition:
SalDettaglio[] salDettaglio
Now I have to put it into session so that I can use it into a JSP view.
How can I do it?
HttpSession session = req.getSession();
session.setAttribute("salDettaglio", salDettaglio);
This puts salDettaglio in the session and you can acces it with the attribute identifier salDettaglio keep in mind that it stores it as a plain Object so you'll have to cast in in the JSP if you are using sciplets.
Also see this question
Beware that Session's getAttribute / setAttribute methods are synchronized, but what you store with them is your responsibility. That means if you change a mutable field in the stored object that is not guaranteed to be seen by another thread. Since servlets are called by different threads from a thread-pool this can be a problem. To avoid this use either immutable objects or make your objects thread-safe.
create one session object and put your array into that session object and using sendRedirect() you can fwd your session through it..!!
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 8 years ago.
I want to send request from html page to servlet then create a list in java servlet then i want to return this list to the same html page .
The problem is that you never initialize your HttpSession s variable:
#WebServlet(urlPatterns = {"/ShowPersonServlet"})
public class ShowPersonServlet extends HttpServlet {
HttpSession s; //null by default
//...
protected void processRequest(...) {
//...
//since never initialized, s is null
user.add((Person) s.getAttribute("person"));
}
#Override
protected void doGet(...) {
//...
//since never initialized, s is null
s.setAttribute("person",person);
}
}
Make-it-work solution: Set the value of s
s = request.getSession();
Real world solution:
Remove all the fields from the Servlet, NEVER try to handle state in your servlet unless they are resources managed by the container like EJBs.
Change the scope of your HttpSession s to be local per method. Also, change its name from s to session or something more useful.
Move your HTML code to a component that deals with view details like a JSP, then perform a forward to the view.
So, your code would look like this:
#WebServlet(urlPatterns = {"/ShowPersonServlet"})
public class ShowPersonServlet extends HttpServlet {
/*
HttpSession s ; //moved as local variable
Person person = new Person(); //moved as local variable
private List<Person> user = new ArrayList<Person>(); //not sure where you want to store this
*/
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
HttpSession session = request.getSession();
List<Person> personList = (List<Person>) session.getAttribute("personList");
if (personList == null) {
personList = new ArrayList<>();
session.setAttribute("personList", personList);
}
personList.add((Person) session.getAttribute("person"));
/*
try (PrintWriter out = response.getWriter()) {
//removed to shorten this answer
}
*/
request.getRequestDispatcher("/showPerson.jsp").forward(request, response);
}
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Person person = new Person();
person.setKey(request.getParameter("txt_Key"));
person.setFirstName(request.getParameter("txt_firstName"));
person.setLastName(request.getParameter("txt_lastName"));
processRequest(request, response);
HttpSession session = request.getSession();
session.setAttribute("person",person);
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
}
More info:
How do servlets work? Instantiation, sessions, shared variables and multithreading
StackOverflow Servlets wiki
You need to get the session from the request, you cannot simply expect to get it with HttpSession s ; - There s is null.
// Something like this (in processRequest), although I
// would prefer a local session variable.
s = request.getSession();
You need to provide stacktrace of your exception. But it looks like it could be in 2 places:
s.setAttribute("person",person); where s is never set;
out.println("" + p.getKey() + ""); where attribute "person" is never set;
Good luck.
The value of var s is not defined. It is null so using s.getAttribute("str") will throw an exception.
HttpSession s ;
s = request.getSession(false);
I wanted to get the HTML Content of processed JSP from controller. I am using Tiles Implementation for Views.
In my scenario, I want to generate the HTML from jsp and send it as JSONP.
So i need to get hold of Generated html from controller.
Here you go!
This worked perfect for me:
#Service
public class CustomViewProcessor
{
private static Logger m_log = LoggerFactory.getLogger(CustomViewProcessor.class);
#Autowired
#Qualifier("tilesViewResolver")
private ViewResolver viewResolver;
#Autowired
#Qualifier("commonInterceptor")
CommonInterceptor commonInterceptor;
public String renderView(HttpServletRequest request, Model model, String viewName)
{
ModelAndView mav = new ModelAndView();
mav.addAllObjects(model.asMap());
mav.setViewName(viewName);
commonInterceptor.updateRequest(mav, request);
try
{
View view = viewResolver.resolveViewName(mav.getViewName(), request.getLocale());
HttpServletResponse localResponse = new MyHttpServletResponseWrapper(new MockHttpServletResponse());
view.render(mav.getModel(), request, localResponse);
return localResponse.toString();
}
catch (Exception e)
{
return "";
}
}
public boolean doesViewExist(HttpServletRequest request, String viewName)
{
try
{
if (viewResolver.resolveViewName(viewName, request.getLocale()) != null)
{
return true;
}
}
catch (Exception e)
{
m_log.error(e.getMessage(), e);
}
return false;
}
static class MyHttpServletResponseWrapper extends HttpServletResponseWrapper
{
private StringWriter sw = new StringWriter();
public MyHttpServletResponseWrapper(HttpServletResponse response)
{
super(response);
}
public PrintWriter getWriter() throws IOException
{
return new PrintWriter(sw);
}
public ServletOutputStream getOutputStream() throws IOException
{
throw new UnsupportedOperationException();
}
public String toString()
{
return sw.toString();
}
}
}
You need to annotate your controller method with #ResponseBody. Please look at the documentation for more details
Control flow leaves the DispatcherServlet and moves to the Servlet Container (e.g. Tomcat) before the JSP page template is populated. So Spring MVC will never have visibility into the final HTML generated from the JSP template.
If you want to pipe your final HTML output into a JSONP request, you're going to do have to implement that as a filter in your Servlet Container, which may or may not support that behavior.
I need to create add servlets at runtime. When I run the following code.
protected void processRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException
{
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
out.println("<html>");
out.println("<head>");
out.println("<title> URI out</title>");
out.println("</head>");
out.println("<body>");
Integer generatedKey = Math.abs(randomiser.nextInt());
out.print(generatedKey);
createServlet(Integer.toString(generatedKey),request.getServletContext());
} finally {
out.println("</body>");
out.println("</html>");
out.close();
}
}
private void createServlet(String generatedKey, ServletContext servletContext) {
String servletMapping = "/"+generatedKey;
ServletRegistration sr = servletContext.addServlet(generatedKey, "com.path.lbs.servlets.testDynamic");
sr.setInitParameter("keyname", generatedKey);
sr.addMapping(servletMapping);
}
I get the following error.
java.lang.IllegalStateException:
PWC1422: Unable to configure mapping
for servlet 1114600676 of servlet
context /123-LBS, because this servlet
context has already been initialized
Is it impossible to add new servlets at runtime i.e. after the Servlet Context is initialised or am I doing something wrong?
Is it impossible to add new servlets at runtime i.e. after the Servlet Context is initialised?
That's correct. You need to do it in ServletContextListener#contextInitialized().
#WebListener
public class Config implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
// Do it here.
}
#Override
public void contextDestroyed(ServletContextEvent event) {
// ...
}
}
However, for your particular functional requirement, a single controller servlet in combination with command pattern is much better suited. You could then add commands (actions) during runtime and intercept on it based on the request URI. See also my answer on Design Patterns web based applications for a kickoff.