I'm overriding a method that belongs to super class. Method takes no parameter. Therefore, I'm unable to pass Objects to the View using Model. Anyone has any suggestion?
#Override
protected String connectView(){
// I'd like to include an object in Model here
// e.g. model.addAttribute(....)
// but unpossible because super does not take a Model as param
return "connect/status";
}
Without having at least the request object, I do not believe you can simply do what you are asking. However, there are a few options:
Use a servlet filter...add the required values to the session there
Use the Decorator Pattern, in which you would create a instance of your class (controller in this case) within another wrapper class. In your wrapper you would do the additional processing, then call the wrapped inner object (your wrapped controller), then do any final processing.
HandlerInterceptor like #sp00m suggests (although I have never used it, so I have no input there)
I am sure there are probably some other options out there, but I can't think of any others.
This should suit your needs, using a HandlerInterceptor, a custom annotation #Model and reflection.
Model.java
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Model {
}
RequestInterceptor.java
#Service
public class RequestInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException {
try {
Class<?> clazz = request.getClass();
if (clazz.isAnnotationPresent(Controller.class)) {
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Model.class)) {
field.set(request, new ModelMap());
break;
}
}
}
} catch (IllegalAccessException e) {
// log("Cannot access model field of controller " + clazz.getSimpleName());
}
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
try {
Class<?> clazz = request.getClass();
if (clazz.isAnnotationPresent(Controller.class)) {
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Model.class)) {
ModelMap model = (ModelMap) field.get(request);
if (model != null) {
modelAndView.addAllObjects(model);
}
break;
}
}
}
} catch (IllegalAccessException e) {
// log("Cannot access model field of controller " + clazz.getSimpleName());
}
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
applicationContext.xml
<!-- register the interceptor -->
<mvc:interceptors>
<bean class="your.package.to.the.RequestInterceptor" />
</mvc:interceptors>
YourController.java
#Controller
public class YourController extends ConnectController {
#Model
private ModelMap model;
#Override
protected String connectView(){
// model is here available
model.addAttribute("attrName", "attrValue");
return "connect/status";
}
}
Related
I'm building a Java REST application with JAX-RS.
In the context of the application, I have the following bean classes:
public class ContentView {
// Field definitions, getters/setters
}
public class ArticleDetailView extends ContentView {
// more definitions, getters/setters
}
public class UpsellDetailView extends ContentView {
// more definitions, getters/setters
}
UpsellDetailView and ArticleDetailView have more attributes and they both extend ContentView. I've got all the mappers correctly wired up, so all the instances of each respective class gets all its properties set correctly. I don't use any extra mappers - object properties get deserialized based on the public getters in the respective bean classes.
SeriesDetailView is similar:
public class SeriesDetailView extends SeriesLiteView {
private List<Content> content;
#JsonIgnore //We don't want content appear on the Series detail page
public List<Content> getContent() {
return content;
}
public void setContent(List<Content> content) {
this.content = content;
}
}
Now, I have a REST resource class as follows:
#Produces({MediaType.APPLICATION_JSON})
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_FORM_URLENCODED})
#Path("/")
public class ApiSiteResource extends AbstractContentResource {
...
#GET
#Path("/series/{uuid}/contents")
public List<ContentView> getSessionsForSeries(#Context HttpServletRequest request, #Context HttpServletResponse response, #BeanParam ApiParams params) {
final SeriesDetailView series = seriesService.findByUuid(params.getUuid());
if (series == null) {
response.setStatus(SC_NOT_FOUND);
return null;
}
List<ContentView> contentViewList = contentMapper.map(series.getContent());
List<ContentView> results = contentViewList.stream().map(e -> mapContent(e, e.getUuid())).collect(Collectors.toList());
return results;
}
#GET
#Path("/contents/{uuid}")
public ContentView uniqueContent(#Context HttpServletRequest request, #Context HttpServletResponse response, #BeanParam ApiParams params) {
ContentView beanView = contentService.findByUuid(params.getUuid());
if (beanView == null) {
response.setStatus(SC_NOT_FOUND);
return null;
}
beanView = mapContent(beanView, params.getUuid());
return beanView;
}
private ContentView mapContent(ContentView beanView, String uuid){
if (ArticleType.TypeGroup.upsell.toString().equals(beanView.getType())) {
UpSell upsell = ((UpSell)contentService.findByUuidRaw(uuid));
beanView = (UpsellDetailView)upsellMapper.map(upsell);
rewriteBodyHtml(beanView, upsell.getBody());
}
else if (ArticleType.TypeGroup.article.toString().equals(beanView.getType()) ||
ArticleType.TypeGroup.audio.toString().equals(beanView.getType()) ||
ArticleType.TypeGroup.video.toString().equals(beanView.getType())) {
Article article = ((Article)contentService.findByUuidRaw(uuid));
beanView = (ArticleDetailView)articleMapper.map(article);
rewriteBodyHtml(beanView, article.getBody());
}
return beanView;
}
}
Now, here's the problem:
when I invoke /contents/{uuid} in the browser, I get fully deserialized correct content type json (Article or UpsellDetailView), but
when I invoke /series/{uuid}/contents, I get a list of short-form json elements corresponding to the ContentView structure.
I do confirm that the List results in getSessionsForSeries() is a list of correct types (Article or UpsellDetailView).
But I can't find for the life of mine why these don't get properly deserialized when in the List. What am I missing?
Here's the answer:
For some reason, List's serializer was messed up and was when serializing was defaulting to the declared content class (ContentView).
If anyone has an idea why I'd appreciate enlightment.
So, I had to use brute force and provide my own serialization (do note the method's response type is changed to String):
#GET
#Path("/series/{uuid}/contents")
public String getSessionsForSeries(#Context HttpServletRequest request, #Context HttpServletResponse response, #BeanParam ApiParams params) {
final SeriesDetailView series = seriesService.findByUuid(params.getUuid());
if (series == null) {
response.setStatus(SC_NOT_FOUND);
return null;
}
List<ContentView> contentViewList = contentMapper.map(series.getContent());
List<ContentView> results = contentViewList.stream()
.map(e -> mapContent(e, e.getUuid())).collect(Collectors.toList());
ObjectMapper objectMapper = new ObjectMapper();
try {
return objectMapper.writeValueAsString(results);
} catch (IOException e) {
response.setStatus(SC_INTERNAL_SERVER_ERROR);
return null;
}
}
Thanks to the tutorial here: http://www.davismol.net/2015/03/05/jackson-json-deserialize-a-list-of-objects-of-subclasses-of-an-abstract-class/
I want to create an abstract controller that will add the additional request mapping basen on actual mappings for the extending controller.
As an example, for the following controller
#Controller
public class MyController extends VariableResolvingController {
#RequestMapping("page.htm")
public void handlerMethod() {
}
}
I want it to extend VariableResolvingControllerthat will add a mapping to it's resolveContextVariable(...) method with the "page.htm.context" URI.
public abstract class VariableResolvingController {
public final #ResponseBody Object resolveContextVariable(String variableName) {
return "{}";
}
protected final void registerVariableResolver(String variableName, VariableResolver resolver) {
//...
}
}
This approach adds a possibility to resolve custom variables using eg. AJAX requests in a way almost transparent for a client code.
Do you know any existing solutions that would be appropriate in this case?
Solution: I achieved my goal by writing a custom HandlerMapping implementation (in essence a decorator for RequestMappingHandlerMapping).
One way of doing is add simple Servlet filter to your spring mvc
public class RequestCheckFilter implements Filter {
#Override
public void destroy() {
// ...
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
//
}
#Override
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
HttpServletRequest httpServletRequest = ((HttpServletRequest) request);
String requestURI = httpServletRequest.getRequestURI();
if (requestURI.endsWith(".context")) {
request.getRequestDispatcher(requestURI.concat(".htm"))
.forward(request,response);
} else {
chain.doFilter(httpServletRequest, response);
}
} catch (Exception ex) {
request.setAttribute("errorMessage", ex);
request.getRequestDispatcher("/WEB-INF/views/jsp/error.jsp")
.forward(request, response);
}
}
add it
public class MyWebInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
//...
#Override
protected Filter[] getServletFilters() {
return new Filter[]{new RequestCheckFilter()};
}
}
or in web.xml
<filter>
<filter-name>reqHandlerFilter</filter-name>
<filter-class>RequestCheckFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>reqHandlerFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
I have configured spring boot for rest controller.
I created many api but i need to validate my token information in every api at begging, Is user is authorized or not base on provided token.
During the signin i am generating token that token required in every api for accessing information. if token is not valid then i need to return message Sorry, your provided token information has been expired or not exists.
below is the my api.
#RequestMapping(value="/delete", method= RequestMethod.DELETE)
public Map<String, Object> delete(#RequestBody String reqData,HttpServletRequest request) {
Map<String, Object> m1 = new HashMap<String,Object>();
JSONObject jsonData = new JSONObject(reqData);
Token token= tokenDao.getByTokenCode(jsonData.getString("token"));
if(token==null){
m1.put("status", "error");
m1.put("message", "Sorry, your provided token information expired or not exists.");
return m1;
}
//here my logic to remove user from database.
}
Is there any way to check token functionality in service method or using annotation, so i need to remove that same code in every api and need to use one common functionality.
you can use HandlerInterceptor to handle you token.
HandlerInterceptor.preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) will execute before any RequestMapping.
validate you token in preHandle.if token is valid continue,else throw exception,controller advice will handler the rest.
expose bean class of MappedInterceptor,spring will auto load HandlerInterceptor bean contains.
ControllerAdvice and ExceptionHandler can catch exception and return error message
full example
#RestController
#EnableAutoConfiguration
public class App {
#RequestMapping("/")
public String index() {
return "hello world";
}
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
public static class MyException extends RuntimeException {
}
#Bean
#Autowired
public MappedInterceptor getMappedInterceptor(MyHandlerInterceptor myHandlerInterceptor) {
return new MappedInterceptor(new String[] { "/" }, myHandlerInterceptor);
}
#Component
public static class TestBean {
public boolean judgeToken(HttpServletRequest request) {
String token = request.getParameter("token");
if (token == null) {
throw new MyException();
}
return true;
}
}
#Component
public static class MyHandlerInterceptor implements HandlerInterceptor {
#Autowired
TestBean testBean;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return testBean.judgeToken(request);
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) throws Exception {
}
}
#ControllerAdvice
public static class MyExceptionHandler {
#ExceptionHandler(MyException.class)
#ResponseBody
public Map<String, Object> handelr() {
Map<String, Object> m1 = new HashMap<String, Object>();
m1.put("status", "error");
m1.put("message", "Sorry, your provided token information expired or not exists.");
return m1;
}
}
}
public class TokenVallidation
{
public static boolean tokenValidation(user id, String token){
Token token= tokenDao.getByTokenCode(id,jsonData.getString("token"));
if(token==null){
m1.put("status", "error");
m1.put("message", "Sorry, your provided token information expired or not exists.");
return false;
}
else{
return true;
}
}
}
for controller pass user id and token and check the token. you need to update dao method as per user id parameter.
Instead of getting token from database and matching with current token you can use cache. create your own cache object like Map or a static string, which will have the latest token. and you can direct compare incoming token with this token from cache. no need to hit database for every time.
In my Jersey application, I'd like to have a ContainerRequestContext instance injected into various objects. In the case that the object in being created outside of the context of a request, I would like null to be injected.
I noticed HK2 has an #Optional annotation that you can annotate dependencies with, and I was hoping that would do the job for, unfortunately it doesn't change the behaviour at all.
public class MyObject {
private final ContainerRequestContext containerRequestContext;
#Inject
public MyObject(#Optional ContainerRequestContext containerRequestContext) {
this.containerRequestContext = containerRequestContext;
}
}
If this object is instantiated outside of a request scope (in my case, a Job run by a Quartz scheduler), then an exception like this gets thrown:
java.lang.IllegalStateException: Not inside a request scope.
It would massively simplify my code if Jersey would just inject null when outside of a request scope, any ideas how to do this?
I've figured out a way of doing it, but it's basically a hack. Instead of having ContainerRequestContext injected, you can instead try to explicitly get a ContainerRequestContext instance from the ServiceLocator, and handle the exception when the context is outside of a request scope.
public class MyObject {
private final Optional<ContainerRequestContext> containerRequestContext;
#Inject
public MyObject(ServiceLocator serviceLocator) {
this.containerRequestContext = getContainerRequestContext(serviceLocator);
}
private Optional<ContainerRequestContext> getContainerRequestContext(ServiceLocator serviceLocator) {
try {
return Optional.of(serviceLocator.getService(ContainerRequestContext.class));
} catch (MultiException e) {
if (e.getCause() instanceof IllegalStateException) {
return Optional.empty();
} else {
throw new ExceptionInInitializerError(e);
}
}
}
}
It's then possible to go one step further and create your own OptionalContainerRequestContext type.
public class OptionalContainerRequestContext {
private final Optional<ContainerRequestContext> containerRequestContext;
#Inject
public OptionalContainerRequestContext(ServiceLocator serviceLocator) {
this.containerRequestContext = getContainerRequestContext(serviceLocator);
}
public ContainerRequestContext get() {
return containerRequestContext.get();
}
public boolean isPresent() {
return containerRequestContext.isPresent();
}
private Optional<ContainerRequestContext> getContainerRequestContext(ServiceLocator serviceLocator) {
try {
return Optional.of(serviceLocator.getService(ContainerRequestContext.class));
} catch (MultiException e) {
if (e.getCause() instanceof IllegalStateException) {
return Optional.empty();
} else {
throw new ExceptionInInitializerError(e);
}
}
}
}
You can then bind it:
bind(OptionalContainerRequestContext.class).to(OptionalContainerRequestContext.class);
And then inject it wherever you need:
public class MyObject {
private final OptionalContainerRequestContext optionalContainerRequestContext;
#Inject
public MyObject(OptionalContainerRequestContext optionalContainerRequestContext) {
this.optionalContainerRequestContext = optionalContainerRequestContext;
}
}
The simple way to deal with optional injection is to #Inject into javax.enterprise.inject.Instance<T>, and then to call instance.isUnsatisfied() before instance.get().
I would like to know how am I able to redirect the request inside the controller constructor if I need to do it?
For example: Inside the constructor I need to check some condition and if doesn't met I want to redirect to some other place. At the same way the rest of the constructor will not be executed neither the "original following action". I want the code like this.
#Controller
class SampleController{
public SampleController(){
if(![some condition]){
...redirecting code...
}
...rest code...
}
...rest code...
}
EDIT
If constructor is not a good option or approach then is there any option like before filter that will execute always before every action of a constructor and will redirect on the failure of some conditions?
You could use an interceptor:
public class CheckInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException {
if (handler instanceof TheController) {
// or for any controller: if (handler.getClass().isAnnotationPresent(Controller.class))
if (!check()) {
redirect("/check-failure.html");
return false;
}
}
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
private void redirect(HttpServletRequest request, HttpServletResponse response, String path) throws ServletException {
try {
response.sendRedirect(request.getContextPath() + path);
} catch (IOException e) {
throw new ServletException(e);
}
}
private boolean check() {
return ...
}
}
Then register it within the applicationContext.xml:
<mvc:interceptors>
<bean class="your.package.CheckInterceptor" />
</mvc:interceptors>