Resolving views in Spring MVC with Freemarker - including jsp page - spring

When using the FreemarkerServlet, it is possible to include JSP pages alongside Freemarker content. However, I'm using Freemarker as a view resolver in my Spring MVC application and so don't use the FreemarkerServlet. Is there any way I can still use #include_page in my Freemarker templates when I'm not using the FreemarkerServlet?
Thanks,
Andrew

I found the solution. You need to sub-class the FreemarkerView to add the include behaviour:
public class CustomFreeMarkerView extends FreeMarkerView
{
private TaglibFactory taglibFactory;
private ServletContextHashModel servletContextHashModel;
#Override
protected void doRender( Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response )
throws Exception
{
// Expose model to JSP tags (as request attributes).
exposeModelAsRequestAttributes( model, request );
// Expose all standard FreeMarker hash models.
model.put( FreemarkerServlet.KEY_JSP_TAGLIBS, this.taglibFactory );
model.put( FreemarkerServlet.KEY_APPLICATION,
this.servletContextHashModel );
model.put( FreemarkerServlet.KEY_SESSION, buildSessionModel( request,
response ) );
model.put( FreemarkerServlet.KEY_REQUEST, new HttpRequestHashModel(
request, response, getObjectWrapper() ) );
model.put( FreemarkerServlet.KEY_REQUEST_PARAMETERS,
new HttpRequestParametersHashModel( request ) );
model.put( FreemarkerServlet.KEY_INCLUDE, new IncludePage( request,
response ) );
if ( logger.isDebugEnabled() )
{
logger.debug( "Rendering FreeMarker template [" + getUrl()
+ "] in FreeMarkerView '" + getBeanName() + "'" );
}
// Grab the locale-specific version of the template.
Locale locale = RequestContextUtils.getLocale( request );
processTemplate( getTemplate( locale ), new SimpleHash( model ), response );
}
private HttpSessionHashModel buildSessionModel( HttpServletRequest request,
HttpServletResponse response )
{
HttpSession session = request.getSession( false );
if ( session != null )
{
return new HttpSessionHashModel( session, getObjectWrapper() );
}
else
{
return new HttpSessionHashModel( null, request, response,
getObjectWrapper() );
}
}
}
The key line:
model.put( FreemarkerServlet.KEY_INCLUDE, new IncludePage( request,
response ) );
and then sub-class the view resolver to utilise the custom view:
public class CustomFreeMarkerViewResolver extends FreeMarkerViewResolver
{
public CustomFreeMarkerViewResolver()
{
setViewClass( requiredViewClass() );
}
/**
* Requires {#link FreeMarkerView}.
*/
#Override
protected Class requiredViewClass()
{
return CustomFreeMarkerView.class;
}
}

This post was very helpful. I'm not sure if this will help, but here's my solution:
Here's my doRender method:
#Override
protected void doRender( Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response )
throws Exception
{
//Expose model to JSP tags (as request attributes).
exposeModelAsRequestAttributes(model, request);
// Expose all standard FreeMarker hash models.
SimpleHash fmModel = buildTemplateModel(model, request, response);
// add the include_page directive
fmModel.put( FreemarkerServlet.KEY_INCLUDE, new IncludePage( request, response));
if (logger.isDebugEnabled()) {
logger.debug("Rendering FreeMarker template [" + getUrl() + "] in FreeMarkerView '" + getBeanName() + "'");
}
// Grab the locale-specific version of the template.
Locale locale = RequestContextUtils.getLocale( request );
processTemplate(getTemplate(locale), fmModel, response);
}

drewzilla's answer is almost worked for me. but i had to use
getTemplate(locale).process(model, response.getWriter());
instead of
processTemplate( getTemplate( locale ), new SimpleHash( model ), response );
with spring

Related

Handle Sharp In Controller And Get Id

There was a jsp application. I have just converted to spring boot application. I want to continue to use same links to handle company's information. Old urls are like /Dashboard.jsp#/company/10712. I have tried to handle company id but it didn't wook. How can I handle company id ?
#GetMapping("/Dashboard.jsp#/company/{id}")
public void try(#PathVariable String id) {
System.out.println(id);
}
I have also tried;
adding
server.tomcat.relaxed-path-chars=#
in application properties.
#RequestMapping(value = ERROR_PATH, produces = "text/html")
public Object errorHtml(HttpServletRequest request, HttpServletResponse response) {
if (response.getStatus() == HttpStatus.NOT_FOUND.value()) {
return new ModelAndView("redirect:" + StringUtils.getBaseUrl(request) + "/?page=error", HttpStatus.FOUND);
} else {
return new ModelAndView("redirect:" + StringUtils.getBaseUrl(request) + "/?page=error");
}
}
This function handle 404.
request.getAttribute("javax.servlet.forward.request_uri")
returns /esir/Dashboard.jsp. There is no # and others.

Spring interceptor intercepting late

Spring boot 2.1.7 running a test server -- need to check my cache for a hit using the url as key, and then act based on a cache hit or not.
I send a request from my browser for https://localhost:8443/test/param=value --- my filter picks it up, and using code from another answer on SO, the filter constructs the url -- the filter code sees the url is https://localhost:8443/test?param=value
Great!
Then my interceptor gets hit (thanks to Theo on SO), but it thinks the url is https://localhost:8443/favicon.ico -- what's up with that? Not much of an interceptor if I didn't get to intercept the original /test url.
To get around that, in the filter, I stored the "real" url in the ServletContext, and that variable is read out correctly in the interceptor. Seems like an awful hack, and silly that I have to do it. For now I've hard-coded the decision to redirect to url /test2, but back in Chrome, I see the output from test1, not test2.
The network tab in Chrome seems to suggest:
that I was redirected to test2, but only after a request for favicon got inserted (for whatever mysterious reason) and yet as the image shows, the output is clearly test1, not test2.
Something I don't understand is that devtools also shows a response from test2:
#WebFilter( urlPatterns = "/test", description = "a filter for test servlet", initParams = {
#WebInitParam( name = "msg", value = "==> " ) }, filterName = "test filter" )
public class TestFilter implements Filter
{
private FilterConfig filterConfig;
#Override
public void doFilter( ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain )
throws IOException, ServletException
{
String url = getCurrentUrlFromRequest( servletRequest );
// in the debugger, url is correctly shown as
// https://localhost:8443/test/param=value
if ( null != url )
{
ServletContext s = servletRequest.getServletContext();
s.setAttribute( "realUrl", url );
}
servletResponse.getOutputStream().print( filterConfig.getInitParameter( "msg" ) );
filterChain.doFilter( servletRequest, servletResponse );
public String getCurrentUrlFromRequest( ServletRequest request )
{
if ( !( request instanceof HttpServletRequest ) ) return null;
return getCurrentUrlFromRequest( (HttpServletRequest) request );
}
public String getCurrentUrlFromRequest( HttpServletRequest request )
{
StringBuffer requestURL = request.getRequestURL();
String queryString = request.getQueryString();
if ( queryString == null ) return requestURL.toString();
return requestURL.append( '?' ).append( queryString ).toString();
}
#Override
public void destroy()
{
}
#Override
public void init( FilterConfig filterConfig ) throws ServletException
{
this.filterConfig = filterConfig;
}
}
//then the interceptor:
#Component
public class CheckForCacheInterceptor implements HandlerInterceptor
{
#Bean
public MappedInterceptor myInterceptor()
{
CheckForCacheInterceptor ci = new CheckForCacheInterceptor();
ci.setRedirectMapping( "/test2" );
return new MappedInterceptor( null, ci );
}
private String redirectMapping;
#Override
public boolean preHandle( HttpServletRequest request, HttpServletResponse response, Object handler )
{
String url = (String) request.getServletContext().getAttribute( "realUrl" );
// "realUrl" has https://localhost:8443/test/param=value, but I'd like
// to get rid of hack. Problem is that right here, running the same
// exact code (copy/paste of filter's
// getCurrentUrlFromRequest( HttpServletRequest request ) method )
//which gets the correct url in the filter yields
// https://localhost:8443/favicon.ico -- where did that come from?
// TODO check cache using requestUrl as key
boolean foundInCache = false;
if ( foundInCache )
{
// TODO: somehow write cache value to response
// then send response
return false;
} else
{
try
{
// TODO: make direct request,
// get response body, then
response.sendRedirect( redirectMapping );
return false;
} catch ( IOException e )
{
e.printStackTrace();
}
}
return false;
}
So before my questions pile up to the ceiling, I'll ask for help -- how is this favicon request sneaking in before my interceptor even has a crack at the original url, why can't I get the original url in my interceptor, and given that the Chrome devtools shows I am getting through to test2, how is the output coming from the test1 servlet instead of the test2 servlet?
FWIW, I'm getting the exact same behavior in Postman. Thanks so much for any help!
I read another answer on SO (sorry I don't have the link) which has fixed the issue of not intercepting the initial get request to /test -- it said that interceptors only intercept requests going to Controllers, so I needed to have a Controller which was mapped to /test. After writing a quickie Controller, the interceptor is now intercepting as one might expect.

Spring RestController handle Get Request with Body values

i currently develop an experimental prototype an wanted to ask if there is a way to accept a response body in a get request.
#RequestMapping(method=RequestMethod.GET, path="/stair/shippingorders", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<?> getShippingOrder(#RequestBody JsonNode request) throws JsonProcessingException, IOException{
log.info("get body: " + request);
// do stuff
return ResponseEntity.ok(response);
}
the test should looks something like this
#Test
public void shouldAcceptRequestBodyinGetRequest() {
JSONObject body = new JSONObject();
body.appendField("stuff", "{}");
HttpEntity<JSONObject> entity = new HttpEntity<JSONObject>(body);
ResponseEntity<String> result = restTemplate.exchange(GET_URL,HttpMethod.GET, entity, String.class );
assertNotNull(result);
}
GET method doesn't support body, hence it won't be possible to send body as part of request. The common practice is to use POST instead

Retrofit returns content length 0 with spring data rest

I am using spring boot application with spring data rest deployed on heroku. I have a /api/userDatas end point on which an entity can be created by a POST request. I have tested it using Postman and it gets created.
Now I am using retrofit on android to perform the same functionality. But the problem is that entity gets created on the server but onFailure() always gets called. I have debugged and found that content-length is always 0.
CreateUser
private void createUser() {
Gson gson = new GsonBuilder()
.setLenient()
.create();
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(logging).build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ServeNetworking.ServeURLS.getBaseURL())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
ServeNetworking serveNetworking = retrofit.create(ServeNetworking.class);
Call<UserData> getUserByIdResponseCall = serveNetworking.socialConnectAPI(userData);
getUserByIdResponseCall.enqueue(new Callback<UserData>() {
#Override
public void onResponse(Call<UserData> call, Response<UserData> response) {
Log.d("getUserById", "onResponse");
Toast.makeText(OTPVerificationActivity.this, "userId : onResponse", Toast.LENGTH_LONG).show();
/**
* Save data in local db and navigate to map screen.
*/
ActivityAnimationUtils.presentActivity(OTPVerificationActivity.this, MapActivity.class);
try{
DBManager.getDBManager(OTPVerificationActivity.this).setIsVerified(true);
} catch (Exception e){
e.printStackTrace();
}
}
#Override
public void onFailure(Call<UserData> call, Throwable t) {
Log.d("getUserById", "onFailure");
Utils.showSnackBar(OTPVerificationActivity.this,"Somethign went wrong", root);
}
});
}
and interface:
public interface ServeNetworking {
#POST("/api/userDatas")
#Headers({ "Content-Type: application/json;charset=UTF-8"})
Call<UserData> socialConnectAPI(
#Body UserData user
);
#GET("/api/userDatas/{userId}")
#Headers({ "Content-Type: application/json; charset=utf-8"})
Call<UserData> getUser(
#Path("userId") String userId
);
/**
* this class is used to get the donor base urls...
*/
class ServeURLS{
private static String baseURL="https://fabrimizer-serve.herokuapp.com";
public static String getBaseURL() {
return baseURL;
}
}
}
I am getting following error:
java.io.EOFException: End of input at line 1 column 1 path $
Found the solution.
added Accept header in ServeNetworking:
#POST("/api/userDatas")
#Headers({ "Content-Type: application/json,"Accept: application/json"})
Call<UserData> socialConnectAPI(
#Body UserData user
);

CXF #PUT request "No message body writer has been found" exception

I am completely stuck! spends many hours on this with no progress...
I have a Spring 4 (4.2.3.RELEASE) app with CXF 3 (3.1.4) which I am trying to JUnit test. Everything is working great except for the PUT request. I am getting the following error:
Caused by: org.apache.cxf.interceptor.Fault: No message body writer has been found for class com.someproject.logic.api.data.User, ContentType: application/xml
at org.apache.cxf.jaxrs.client.WebClient$BodyWriter.doWriteBody(WebClient.java:1222)
at org.apache.cxf.jaxrs.client.AbstractClient$AbstractBodyWriter.handleMessage(AbstractClient.java:1091)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
at org.apache.cxf.jaxrs.client.AbstractClient.doRunInterceptorChain(AbstractClient.java:649)
at org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1093)
... 49 more
Caused by: javax.ws.rs.ProcessingException: No message body writer has been found for class com.someproject.logic.api.data.User, ContentType: application/xml
at org.apache.cxf.jaxrs.client.AbstractClient.reportMessageHandlerProblem(AbstractClient.java:780)
at org.apache.cxf.jaxrs.client.AbstractClient.writeBody(AbstractClient.java:494)
at org.apache.cxf.jaxrs.client.WebClient$BodyWriter.doWriteBody(WebClient.java:1217)
... 53 more
I also tried with "application/json" and got the same result.
Here is the CXF configuration:
#Bean
#DependsOn("cxf")
public Server jaxRsServer() {
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
List<Object> providers = new ArrayList<Object>();
// get all the class annotated with #JaxrsService
List<Object> beans = configUtil.findBeans(JaxrsService.class);
if (beans.size() > 0) {
// add all the CXF service classes into the CXF stack
sf.setServiceBeans(beans);
sf.setAddress("http://localhost:8080/api");
sf.setBus(springBus);
sf.setStart(true);
// set JSON as the response serializer
JacksonJsonProvider provider = new JacksonJsonProvider();
providers.add( provider );
}
// add custom providers if any
sf.setProviders(providers);
return sf.create();
}
Endpoint:
#Path("/user")
#PUT
#Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, })
#Produces( MediaType.APPLICATION_JSON )
public User updateUser( User user );
Endpoint impl:
#Override
public User updateUser( User user ) {
System.out.println( "updateUser: " + user );
return user;
}
Test:
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
webClient = WebClient.create( "http://localhost:8080/api" );
WebClient.getConfig( webClient ).getRequestContext().put( LocalConduit.DIRECT_DISPATCH, Boolean.TRUE );
webClient.accept( "application/json" );
}
#Test
public void testPut() {
String apiUrl = "/user";
webClient.path( apiUrl );
User user = createDummyAPIUser();
try {
System.out.println( "testUpdateUser: PUT request to " + apiUrl );
String response = webClient.type( MediaType.APPLICATION_JSON ).put( user, String.class );
System.out.println( "testUpdateUser: " + apiUrl + " response: " + response );
//updatedUser = (new ObjectMapper()).readValue( response, User.class );
} catch( Exception e ) {
e.printStackTrace();
fail();
return;
}
}
First how do I make sure that the fasterxml Jackson 2 provider is in fact serializing the message body? It do not see anything Jackson in the stacktrace but I do set it in the providers.
I have found this link to demonstrate a custom ContextProvider, is this the only way to get this working? Seem utterly redundant...
http://www.blackpepper.co.uk/custom-context-providers-for-cxf-with-the-context-annotation/
Any ideas?
Thank you!!
GEEEEEEZZZ...
feeling like a dork, forgot to add the same Jackson serializer ( provider ) to the client. Looking again through the stacktrace I noticed that the methods were only from the client, so obviously the client did not know how to consume the POJO I was throwing at it...
Updated test code:
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
final List<Object> providers = new ArrayList<Object>();
JacksonJaxbJsonProvider jacksonJsonProvider = new JacksonJaxbJsonProvider();
providers.add( jacksonJsonProvider );
webClient = WebClient.create( "http://localhost:8080/api", providers );
WebClient.getConfig( webClient ).getRequestContext().put( LocalConduit.DIRECT_DISPATCH, Boolean.TRUE );
webClient.accept( "application/json" );
}

Resources