I have an email utility class that is shared by all my intranet web apps which emails an employee their forgotten password (well the class is copied into each webapp). I need to brand the email with appropriate Subject Line, ReplyTo Point Of Contact, Application Name, etc that matches the app that is calling it.
I'm able to pass these as parameters but the way I do it is by including an initialize.jsp in my header on the login webpage.
<% request.setAttribute("siteEmail", "Commitment#<domain>.com");
request.setAttribute("siteName", "Commitment Report Database");
request.setAttribute("sitePOCEmail", "smith#<domain>.com");
%>
These request parameter are then stored into a DTO by a Struts similar Action
public class STKRequestPasswordAction implements ControllerAction {
public STKRequestPasswordAction() {}
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ApplicationConfig ac = new ApplicationConfig();
ac.setAppEmail(request.getAttribute("siteEmail").toString());
ac.setAppName(request.getAttribute("siteName").toString());
ac.setPocEmail(request.getAttribute("sitePOCEmail").toString());
request.setAttribute("ac", ac);
userForm.requestPassword(LoginUser, ac);
The userForm.requestPassword method
public void requestPassword(STKUser loginUser, ApplicationConfig ac) {
validates the inputed userID requesting the password, builds the StringBuffer message, and then calls the Email Utility class.
EmailUtils.sendEMailByBadge(loginUser.getBadge(), strFrom, strReplyTo, strSubject, emailBody.toString(), strSMTP, true);
I'd like to refactor my initializing parameters out of the included jsp and put them into the web.xml, but I am not sure where I should be reading the values. I tried this in the POJO
String strFrom = getServletContext().getInitParameter("siteEmail");
but there is no servletContext in the POJO. I would imagine it could go in my servlet but my servlet uses a mapping as follows:
actionMap.put(null, new STKLoginSubmitAction());
actionMap.put("chkpass", new STKLoginSubmitAction());
actionMap.put("sqlReset", new STKRequestPasswordAction());
String op = request.getParameter("method");
ControllerAction action = (ControllerAction) actionMap.get(op);
I plan to move ApplicationConfig ac to the servlet and change the method signature for STKRequestPasswordAction to pass `ac'. Is this the best approach or am I missing the picture?
In your web.xml:
<env-entry>
<env-entry-name>myEntryName</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>myEntryValue</env-entry-value>
</env-entry>
In your Java code:
// Do once
Context env = (Context) new InitialContext().lookup("java:comp/env");
// Do for every entry
String entryValue = (String) env.lookup("myEntryName");
Using JNDI you can read your settings every where in your webapp and also keep your configs out of your web.xml if you need, in context.xml for Tomcat or jetty-env.xml for Jetty for example (though format is different).
Related
I have a simple Spring Boot REST service for the IFTTT platform. Each authorized request will contain a header IFTTT-Service-Key with my account's service key and I will use that to either process the request or return a 401 (Unauthorized). However, I only want to do this for select endpoints -- and specifically not for ANY of the Spring actuator endpoints.
I have looked into Spring Security, using filters, using HandlerInterceptors, but none seem to fit what I am trying to do exactly. Spring security seems to come with a lot of extra stuff (especially the default user login), filters don't really seem to match the use case, and the handler interceptor works fine but I would have to code logic in to watch specific URLs and ignore others.
What is the best way to achieve what I am trying to do?
For reference, this is the code I have now:
public class ServiceKeyValidator implements HandlerInterceptor {
private final String myIftttServiceKey;
public ServiceKeyValidator(#Value("${ifttt.service-key}") String myIftttServiceKey) {
this.myIftttServiceKey = myIftttServiceKey;
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// TODO will have to put logic in to skip this when actuator endpoints are added
String serviceKeyHeader = request.getHeader("IFTTT-Service-Key");
if (!myIftttServiceKey.equals(serviceKeyHeader)) {
var error = new Error("Incorrect value for IFTTT-Service-Key");
var errorResponse = new ErrorResponse(Collections.singletonList(error));
throw new UnauthorizedException(errorResponse);
}
return HandlerInterceptor.super.preHandle(request, response, handler);
}
}
You need to add filtering for the required endpoints in the place where you register your HandlerInterceptor.
For example:
#EnableWebMvc
#Configuration
public class AppConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(
new ServiceKeyValidator())
.addPathPatterns("/ifttt/**")
.excludePathPatterns("/actuator/**");
}
}
You can use different URLs path matchers to filter which URL endpoints must be handled by your interceptor and which are not. As the method addPathPatterns returns InterceptorRegistration object that configures this.
I have the following Spring controller:
#Controller
public class TypoWorkflowController {
#RequestMapping(value = "/workflow/typo-workflow/moreInfo", method = RequestMethod.GET)
public String serveMoreInfo(#RequestParam(value = "taskId", required=false) String taskId, ModelMap modelMap) {
return "typo-workflow-more-info";
}
}
My tiles-def file contains:
<definition name="typo-workflow-more-info" template="/WEB_INF/jsp/workflow/typo-workflow/moreInfo.jsp"/>
My JSP is plain old HTML.
When I hit the url /workflow/typo-workflow/moreInfo, Tomcat throws a StackOverflowError.
When I step through in debug mode, I see that I'm hitting my controller first, as I would expect, but then I hit another controller, at the method:
#Controller
#Order(value = Ordered.LOWEST_PRECEDENCE)
public class ContentServingController {
/* ... */
#RequestMapping({"/*", "/**/*"})
public ModelAndView serveContent(HttpServletResponse response, ModelMap model) {
/* ... */
}
}
As I poked around, it seeeeeeemed like we were in there to respond to a request for /WEB_INF/jsp/workflow/typo-workflow/moreInfo.jsp, but this doesn't happen for other controllers that operate in the same way (returning a View name).
So, can anyone provide me with some pointers for debugging this. Why would I be hitting a controller for a JSP anyway? Isn't a JSP supposed to be a little servlet itself?
Your tiles def is pointing to the WEB_INF folder when it should be pointing to the WEB-INF folder (dash instead of underscore) so spring doesn't know where to look within the app and is just making a normal http request, which is getting caught by the wildcard match.
I have a requirement where the back-end Spring controller is expecting certain values in the request header, i.e certain custom headers.
The view, a jsp has a html form which will submit the parameters from the end user to the controller. Since it is not possible to send custom headers from an html form or javascript function without using XMLHttpHeader. I cannot use XMLHttpHeader in this case since that will be an AJAX call and in my case i want a form submit.
The only option left for me is, add a mediator servlet which will intercept the call from the jsp, read the values from the request parameter and then add them as request headers and forward them to the final controller. Since it is not possible to add custom header in HttpServletRequest, i used HttpServletRequestWrapper as given in the
example.
The problem is to forward it to the Spring controller , the following code
MockHttpRequestWrapper req = new MockHttpRequestWrapper(request);
req.addHeader("REMOTE_USER", ssoId);
req.addHeader("REALM", realmId);
RequestDispatcher dispatcher = request.getRequestDispatcher(url);
dispatcher.forward(req, response);
where MockHttpRequestWrapper is a type of HttpServletRequestWrapper as per the example
public class MockHttpRequestWrapper extends HttpServletRequestWrapper {
private Map customHeaderMap = null;
public MockHttpRequestWrapper(HttpServletRequest request) {
super(request);
customHeaderMap = new HashMap();
}
public void addHeader(String name,String value){
customHeaderMap.put(name, value);
}
#Override
public String getParameter(String name) {
String paramValue = super.getParameter(name); // query Strings
if (paramValue == null) {
paramValue = (String) customHeaderMap.get(name);
}
return paramValue;
}
}
I haven't tested the sample with Filters but i expected the same to work with RequestDispatchers. The final controller is called but the headers are not to be found.
Is the same Request object not passed to the controller? IN the controller the code
request.getHeader("REMOTE_USER")
returns null.
Looking forward to answers to expand my understanding on the subject :)
I just want to have 2 functions in JAVA Spring 3.0 MVC like page1.htm and page2.htm and just 1 controller, kinda is in CodeIgniter, i don't know why is so hard ...
This is what i did till now
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package controllers;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.validation.BindException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
// Import models
import models.LoginModel;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
*
* #author Candy
*/
#RequestMapping("/app")
public class LoginController extends SimpleFormController {
public LoginController()
{
//Initialize controller properties here or
//in the Web Application Context
setCommandClass(models.Employee.class);
setCommandName("Employee");
//setSuccessView("pages/login/login_form_view");
//setFormView("pages/login/login_execute_view");
}
// On form load
#Override
protected ModelAndView showForm(HttpServletRequest request, HttpServletResponse response, BindException errors) throws Exception
{
LoginModel loginModel = new LoginModel(request);
if(!loginModel.isLogged())
{
ModelAndView mv = new ModelAndView("pages/login/login_form_view");
// mv.addObject("chestie",loginModel.isLogged());
return mv;
}
else
{
return new ModelAndView("redirect:/dashboard.htm");
}
}
#Override
protected void doSubmitAction(Object command) throws Exception
{
throw new UnsupportedOperationException("Not yet implemented");
}
//Use onSubmit instead of doSubmitAction
//when you need access to the Request, Response, or BindException objects
#Override
protected ModelAndView onSubmit(
HttpServletRequest request,
HttpServletResponse response,
Object command,
BindException errors) throws Exception
{
// User has submited a POST request
ModelAndView mv = new ModelAndView("pages/login/login_execute_view");
// Check if the user is logged or not, user must be NOT LOGGED in order to log
LoginModel loginModel = new LoginModel(request);
if(!loginModel.isLogged())
{
// Just continue
String email = request.getParameter("email").toString();
String password = request.getParameter("password").toString();
// Check this email/pass
Map<String,String> resultLogin = loginModel.login(email, password);
String result = resultLogin.get("result").toString();
String reason = resultLogin.get("reason").toString();
// String qqq = resultLogin.get("qqq").toString();
/*
java.util.Date dt = new java.util.Date();
java.text.SimpleDateFormat sdf =
new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String currentTime = sdf.format(dt);
*/
if(result == "true")
{
// Login is good, redirect to dashboard
return new ModelAndView("redirect:/dashboard.htm");
// Display the error
//mv.addObject("loginResult",result);
//mv.addObject("loginReason",reason);
//mv.addObject("qqq",qqq);
//mv.addObject("qqq2",request.getSession().getAttribute("is_logged").toString());
}
else
{
// Display the error
mv.addObject("loginResult",result);
mv.addObject("loginReason",reason);
return mv;
}
}
else
{
return new ModelAndView("redirect:/login.htm");
}
}
}
this controller will not work if I remove
applicationContext.xml
<bean class="controllers.LoginController"/>
dispatcher-servlet.xml
<bean class="controllers.LoginController" p:formView="pages/login/login_form_view" p:successView="pages/login/login_execute_view"/>
web.xml
<servlet>
<servlet-name>LoginController</servlet-name>
<servlet-class>controllers.LoginController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginController</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
Why i have to edit 3 XML when i create a new controller? Is not a simpler way?
All i want right now is just make another function logout() that loads the ModelAndView mv = new ModelAndView("pages/login/logout_view");, that's all.
And this
#RequestMapping("/app")
#Override
protected ModelAndView showForm(HttpServletRequest request, HttpServletResponse response, BindException errors) throws Exception
{
ModelAndView mv = new ModelAndView("pages/login/login_form_view");
return mv;
}
gives me 404 error ...
What should i use? SimpleFormController, extend Controller, AbstractController (i'm using NETBEANS). I'm confused, i wish there were a good java framework like CodeIgniter. I don't quite understand how mappings work, but i do know how an apache rewrite works.
Thank you very much.
If you are using Spring 3, you can easily use #controller annotation for your controller class which are used for your page request and navigation.
You can use #Service annotation for your business logic and #Repository for your Database transaction and bind these layers together using #Autowired annotation as well .
so your request goes this way ( UI Request -> #Controller -> #Service -> #Repository ) and come back to the original controller class again for navigation.
But about your question, when you mark a class with #Controller annotation in spring, that simply means you candidate that class for your URL mappings and navigations, the sample below show how you can use #RequestMapping annotation inside Controlller Class for your URL mapping:
#Controller
#RequestMapping("/employee")
public class Employees{
#AutoWired
private EmployeeServiceInterface empServInt;
#ModelAttribute("empAdd")
public Employee createEmployeeModel(){
return new Employee();
}
#RequestMapping(value="/add", method = RequestMethod.POST)
public String addEmployee(#ModelAttribute("empAdd")Employee employee,Model model, HttpSesion session){
Employee employee = empServInt.createUser(employee);
if(yourCondition()){
return "redirect:/TO_THIS_PAGE";
}
return "GO_TO_PAGE_NAVIGATION_HERE";
}
}
note that #RequestMapping get activated when a request come with the URL HOST_NAME:PORT/PROJECT_NAME/employee/add because of #RequesMapping annotation value.
Secondly #ModelAttribute you saw in sample create a model for your page that helps you to submit the Employee Object Bean values from UI using commandName attribute in Spring Form tag which binds the html inputs to Employee Bean class by using path="" attribute :
<form:form commandName="empAdd" method="post" >...</form:form>
Thirdly take note that the #AutoWired can automatically wired an interface to an implementation so in my example if a class like EmployeeServiceImpl implements the interface EmployeeServiceInterface by calling the interface methods like: empServInt.createUser(employee); spring automatically call related EmployeeServiceImpl.createUser(employee) implementation method for you.
Fourthly: take note that for you to be able to use Controller and the Service & Repository annotation to work you should have defined the packages that have those annotations in your Spring Descriptor XML file like so:
<context:component-scan base-package="java.package" />
Fifthly if you noticed the method inside Employees has String return type that provide your navigation but for this to work you should have define the page prefix and suffix that is going to return inside your Spring Descriptor XMl file as well like so:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/" />
<property name="suffix" value=".jsp" />
</bean>
according to above config, if your method which is annotated with #RequestMapping is returning employee String, Spring go and fetch the page from this location:
[PREFIX]employee[POSSIX] --> /WEB-INF/view/employee.jsp
For better understanding I found a useful sample link for you. Link
Good Luck !
Consider a simply servlet:
// MyServlet.java
protected void doGet(HttpServletRequest request, HttpServletResponse response)
{
UtilClass.doSomething(getServletContext().getRealPath(SOME_FILE));
}
And the utility class does something with the file:
// UtilClass.java
public String doSomething(String filePath)
{
File f = new File(filePath);
String s = readWhateverFrom(f);
return s;
}
I am now porting the doSomething() function to a web service running under Tomcat and Axis2. How would I port it so that I can still access the context and get access to a file under the servlet?
You should get ahold of your (jax-ws) MessageContext. This would depend on your configuration, but perhaps using
#Resource
private WebServiceContext wsCtx;
and in your method:
MessageContext messageContext = wsCtx.getMessageContext()
ServletContext ctx = (ServletContext)
messageContext.getProperty(MessageContext.SERVLET_CONTEXT);
Edit: Seems like Axis2 (as well as Axis) support the following:
HttpServlet servlet = (HttpServlet)
MessageContext.getCurrentContext().getProperty(HTTPConstants.MC_HTTP_SERVLET);
ServletContext ctx = servlet.getServletContext();
With the following imports:
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.transport.http.HTTPConstants;
Sounds like a job for a Servlet Filter and a ThreadLocal. Axis is running within a Servlet Context, too. So all you have to do is to implement a custom javax.servlet.Filter, stuffing in the ServletRequest into a ThreadLocal where you can access it from within your utility class. You can get the ServletContext from the FilterConfig.