Spring 3 Controller and Relative Paths - rest

Trying to get to grips with Spring MVC Controllers. But having a problem with relative paths
I have a page at:
http://localhost:8080/jeeniweb/articles
on that page there is a menu option :
<li>Structure and Dependencies</li>`
This, in a browser, resolves to:
http://localhost:8080/jeeniweb/articles/writing_great_code/structure_and_dependencies/
When a user clicks on this link I intercept this request with a controller:
#RequestMapping(value = "/articles/{article}/{chapter}")
public String articles(#PathVariable String article, #PathVariable String chapter)
{
System.out.println("Articles Page Request");
System.out.println("article: " + article);
System.out.println("chapter: " + chapter);
return "articles/index";
}
This method catches the request and the println methdods print out the right stuff.
However after the call I want to:
http://localhost:8080/jeeniweb/articles
But in fact the browser goes to:
http://localhost:8080/mysite/articles/writing_great_code/structure_and_dependencies/
How can this be when I am returning articles/index from the method?
My Servlet config is:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
As I understand Spring this means the resultant return after the processing above code should be /WEB-INF/views/articles/index.jsp which is what I want. That's where the index.jsp page is.
Any help is much appreciated.
Thanks
Adam

The controller just knows that it intercepted the correct url, in order to resolve articles/index url, you'd need to have it return a redirect to that url such as:
return "redirect:articles/index"

Related

Spring integration XMPP and Google Cloud Messaging

I'm using the spring integration xmpp module to write a custom implementation of a 3rd party Server connecting to GCM cloud services, as in GCM Cloud Connection Server (XMPP).
So far I've successfully connected to the GCM server, however when I send a message to the server I end up with something like:
<message id="m-1366082849205" to="REGISTRATION_ID">
<body>{"hello":"world"}</body>
</message>
, but I need to send something like this:
<message id="">
<gcm xmlns="google:mobile:data">
{
"to":"REGISTRATION_ID",
"message_id":"m-1366082849205"
"data":
{
"hello":"world",
}
}
</gcm>
</message>
I use the latest SI version, 4.0.4, this is my configuration in the xml:
<int-xmpp:outbound-channel-adapter
id="gcmOutboundAdapter" channel="gcmOutboundNotificationChannel"
xmpp-connection="gcmConnection" auto-startup="true"/>
I'm sending messages with the usual MessageBuilder like this:
Message<String> xmppOutboundMsg = MessageBuilder.withPayload(xmppPayload)
.setHeader(XmppHeaders.TO, REGISTRATION_ID)
.build();
where xmppPayload is a json string.
I need to configure/override the way the xmpp message is composed, what is the best practice to achieve the result? Should I override the class implementing int-xmpp:outbound-channel-adapter with a custom service activator, is there anyway to configure the way the xmpp message is composed?
Thanks for any help.
<gcm xmlns="google:mobile:data"> is a extended content element (see RFC 6120 8.4), which is modelled as PacketExtension in Smack. Do not subclass message, instead create a GCMPacketExtension class and add a instance of it to your message
message.addPacketExtension(gcmPackExtension)
The format of the message is hard-coded in the Smack Message.toXML() method (we use the smack library underneath).
See #Flow's answer.
Then, subclass ChatMessageSendingMessageHandler, overriding handleMessageInternal() - pretty much copy the code and set the extension after the message is created.
The easiest way to configure your custom handler is probably to put it in a chain...
<chain input-channel="gcmOutboundNotificationChannel">
<bean class="foo.MyChatMessageSendingMessageHandler">
<constructor-arg ref="gcmConnection" />
</bean>
</chain>
Or you can wire it up as a top level bean and inject it into a ConsumerEndpointFactoryBean.
Feel free to open a New Feature JIRA Issue and we'll consider adding an extension point to make this a bit easier.
Until we introduce the PackExtension injection, you can overcome it with custom <transformer ref="">, because the <int-xmpp:outbound-channel-adapter> can accept org.jivesoftware.smack.packet.Message as a Message payload:
<transformer ref="toGcmTransformer" output-channel="gcmOutboundNotificationChannel"/>
<int-xmpp:outbound-channel-adapter
id="gcmOutboundAdapter" channel="gcmOutboundNotificationChannel"
xmpp-connection="gcmConnection" auto-startup="true"/>
public class ToGcmTransformer extends AbstractTransformer {
protected Object doTransform(Message<String> message) throws Exception {
String to = message.getHeaders().get(XmppHeaders.TO, String.class);
xmppMessage = new org.jivesoftware.smack.packet.Message(to);
xmppMessage.setBody(message.getPayload());
xmppMessage.addPacketExtension(gcmPackExtension);
return xmppMessage;
}
}
Please, raise an issue about PackExtension support.
-->
<int:chain input-channel="gcmOutboundNotificationChannel">
<!--<int:transformer ref="toGcmTransformer" output-channel="gcmOutboundNotificationChannel"/>-->
<bean class="com.payumoney.cardhash.service.MyMessageSendingMessageHandler">
<constructor-arg ref="gcmConnection" />
</bean>
</int:chain>
<int:transformer id="testTransformer" ref="toGcmTransformer" input-channel="gcmInboundNotificationChannel"
method="doTransform" output-channel="gcmOutboundNotificationChannel"/>
<!--<int:transformer ref="toGcmTransformer" output-channel="gcmOutboundNotificationChannel"/>-->
<int-xmpp:outbound-channel-adapter
id="gcmOutboundAdapter" channel="gcmOutboundNotificationChannel"
xmpp-connection="gcmConnection" auto-startup="true"/>
<int:chain input-channel="gcmInboundNotificationChannel">
<bean class="com.payumoney.cardhash.service.PayumoneyNotificationListeningEndpoint">
<constructor-arg ref="gcmConnection" />
<property name="outputChannel" ref="gcmOutboundNotificationChannel" />
</bean>
</int:chain>
<int-xmpp:inbound-channel-adapter
id="gcmInboundAdapter" channel="gcmInboundNotificationChannel"
xmpp-connection="gcmConnection" extract-payload="true" auto-startup="true" />

Saving WS payload into database in Camel

I'm absolutely newbie in Apache Camel. I'd like to create a very simple app in which it would accept WS calls and save the payload into a database using JPA.
The payload's structure is quite simple. The root is a Marriage object. It contain some String and int and Date fields, a wife, a husband and a list of children (Person objects).
My goal is to save these data into two tables of a database: MARRIAGE, PERSON.
I've successfully created a jaxws:endpoint in which I listen and respond a dummy response.
I've created the tables and JPA entities.
I don't know how to "connect" the WS implementation with the spring configured JpaTemplate. Should I solve this problem with Camel routing using somehow a #Converter class or #Injet it into the WS implementing class by Spring. I'm confused.
Should I use cxf endpoint instead of jaxws endpoint?
You need to use camle-cxf endpoint if you want to use camel. What I would do is expose the endpoint as a camle-cxf endpoint. Something like this:
<camel-cxf:cxfEndpoint id="listenerEndpoint"
address="http://0.0.0.0:8022/Dummy/services/Dummy"
wsdlURL="wsdl/DummyService.wsdl"
xmlns:tns="http://dummy.com/ws/Dummy"
serviceName="tns:Dummy"
endpointName="tns:DummyService">
<camel-cxf:properties>
<entry key="schema-validation-enabled" value="true"/>
<entry key="dataFormat" value="PAYLOAD"/>
</camel-cxf:properties>
</camel-cxf:cxfEndpoint>
Then I would have a simple Spring bean like this:
<bean id="processor" class="com.dummy.DummyProcessor">
<property name="..." value="..."/> //there goes your data source of jdbc template or whatever...
</bean>
If you want to use JPA just configure all the configuration and inject your entity manager into this bean.
The actual class would look something like this:
public class DummyProcessor {
#Trancational //If you need transaction to be at this level...
public void processRequest(Exchange exchange) {
YourPayloadObject object = exchange.getIn().getBody(YourPayloadObject.class);
//object - is your object from SOAP request, now you can get all the data and store it in the database.
}
}
The camel route would be like this:
<camel:camelContext trace="true" id="camelContext" >
<camel:route id="listenerEndpointRoute">
<camel:from uri="cxf:bean:listenerEndpoint?dataFormat=POJO&synchronous=true" />
<camel:log message="Got message. The expected operation is :: ${headers.operationName}"/>
<camel:choice>
<camel:when>
<camel:simple>${headers.operationName} == 'YourPayloadObject'</camel:simple>
<camel:bean ref="processor" method="processRequest"/>
</camel:when>
</camel:choice>
<camel:log message="Got message before sending to target: ${headers.operationName}"/>
<camel:to uri="cxf:bean:someTargetEndpointOrSomethingElse"/>
<camel:log message="Got message received from target ${headers.operationName}"/>
</camel:route>
</camel:camelContext>
Hope this helps.

WSO2 Synapse: setting a URL parameter

I am trying to do something that seems straightforward but can't get it to work. Basically I want the WSO2 API manager to add a URL parameter to a REST call.
Setup and Problem
I have a WSO2 API manager installed. I also have a Tomcat running with a bogus servlet called someservlet that simply logs anything it receives and returns ok. I have added the servlet as an API in the manager, so I can either call the servlet directly or via WSO2 API mgr.
I can run
curl http://localhost:8080/someservlet/servlet/toto?blob=blib&secret=pass
and the servlet works fine, it tells me it's received the path /toto and parameters blob and secret.
I can run
curl -H "Authorization: Bearer [...]" --url "http://192.168.23.1:8280/someservlet/1.0/toto?blob=blib&secret=pass"
And it does exactly the same. So far, so good.
What I want is to run:
curl -H "Authorization: Bearer MqVQuHqLNphtPV3XF1CtXVmbyP8a" --url "http://192.168.23.1:8280/someservlet/1.0/toto?blob=blib"
(note that I've removed the secret parameter)
...and still get the same result.
So basically I want API manager to add the URL parameter secret=pass.
First thing I tried: property mediator
Use Synapse XML config with property REST_URL_POSTFIX.
I edited the API config file, and added
<property name="REST_URL_POSTFIX" value="/blob?toto=titi" scope="axis2" type="STRING"/>
Now if I run
curl -H "Authorization: Bearer [...]" --url "http://192.168.23.1:8280/someservlet/1.0/toti?blab=blib&secret=puss"
it's as if I ran someservlet/1.0/blob?toto=titi: all my path and parameters have disappeared and been repaced with the configured ones. Hey, that's how it's supposed to work, isn't it!
Problems:
this doesn't add something to the URL, it sets the URL postfix, meaning that existing parameters disappear (in the above example, blab=blib)
it has to start with a "/" to be a valid value, so I can't just add &secret=pass (of course, because of problem 1, this would be useless anyway)
So basically this doesn't enable me to append the final &secret=pass.
Second thing I tried: rewrite URL mediator
I found this mediator, and although it probably won't do the trick, it's a good lead: I can just call with secret=foo, and get the mediator to replace it with secret=pass.
I put this in the config file:
<rewrite>
<rewriterule>
<action type="replace" value="pass" fragment="query" regex="foo"/>
</rewriterule>
</rewrite>
This doesn't work. At first I thought I didn't have the action parameters right. But the error message is:
Malformed URL when processing /someservlet/1.0/toti?blab=blib&secret=foo
Malformed? There's more detail in the exception stack trace:
java.net.MalformedURLException: no protocol: /someservlet/1.0/toti?blab=blib&secret=foo
So what's happening is that the mediators (log or rewrite) receive a message whose "To:" field points to a URL with no protocol!
Of course I've been googling around, and there are some cases where I find other people have logMediator: To: /blabla, and other (most) cases where they have logMediator: To: http ://blabla. I don't really see what's causing the difference. :-(
So this is where I'm stuck!! :-(
Option to be tried
I'm aware that there's probably a sledgehammer solution that should work:
use property to store the full path and all parameters
implement my own mediator (e.g. in Java) to modify these parameters
use property REST_URL_POSTFIX to put the modified postfix on the call
However I feel that this problem should have a simpler solution.
I have a kind of hope that someone will point me to a simple resource (mediator, sample, syntax error, anything) that I haven't found and that does just what I want. Optimism... :-)
Thanks for reading. Any ideas?
As i understood, you are trying to append the secret=pass to your endpoint url.
When you invoke the API , i belive you are sending that parameter.
Same time you are sending "blab=blib" parameter also. But in the ESB you need to change that to "toto=titi"
The method you used is right.(ie: Using property REST_URL_POSTFIX) In that case it will replace all requets parameter.
But before using that, you can save the request parameter and using the "REST_URL_POSTFIX"
you can achive the final REST url you need. (yes, that is the final option you mentioned as "to be tried")
In the "TO" address, you will only have address after port number.
If you use "" POST_TO_URI" property yopu will see the full adress URL printed in the log, but we use that if there is any proxy server configured between BE service and ESB
I've solved my problem.
Here's what I do:
in the config file, get the URL postfix using REST_URL_POSTFIX and
put it in a property
edit the property in a Java mediator
use REST_URL_POSTFIX to set the new postfix in XML
For the 1rst step, I use this line:
<property name="querystrings" expression="get-property('axis2', 'REST_URL_POSTFIX')"/>
this took some time to corner, because there are many lines out there that don't work for me, it took the 2 parameters (axis2 and REST...) to get it straight.
For the second step, this is the config XML:
<class name="mypackage.AddUrlParamMediator">
<property name="paramName" value="mykey"/>
<property name="paramValue" value="mysecret"/>
</class>
this is the mediator class (without imports):
public class AddUrlParamMediator extends AbstractMediator {
private String paramName = "default";
private String paramValue = "default";
public void setParamName(String paramName) {
this.paramName = paramName;
}
public void setParamValue(String paramValue) {
this.paramValue = paramValue;
}
public boolean mediate(MessageContext synapseMsgContext) {
SynapseLog log = this.getLog(synapseMsgContext);
String urlNewParam = this.paramName + "=" + this.paramValue;
Object queryStringsPpty = synapseMsgContext.getProperty("querystrings");
if (queryStringsPpty == null) {
log.error("### queryStringPpty=null, exiting!");
return true;
}
String queryStrings = queryStringsPpty.toString();
queryStrings = (queryStrings.contains("?"))
? queryStrings + "&" + urlNewParam
: queryStrings + "?" + urlNewParam;
synapseMsgContext.setProperty("querystrings", queryStrings);
return true;
}
}
and of course here's the third step in the XML config:
<property name="REST_URL_POSTFIX" expression="get-property('querystrings')" scope="axis2" type="STRING"/>
So all this basically sets a parameter at the end of the URL.
Hope this helps others.
For anyone who may have the same issue, here's another solution, simpler and works.
Go to the carbon admin portal, to the list of APIs, find the relevant API and click on it.
This leads to the XML config of the API.
After the "address" field (and at the same level in the XML) add the field:
<property name="Authorization" value="stuff to add" scope="transport"/>
This adds the property "Authorization" with value "stuff".

Spring-MVC: Redirects Not Properly Changing URLS When The Original Ends In A Slash

I'm using Spring 3.1 to renovate a very old Servlet based site. Some URLs have been made obsolete. My boss doesn't trust the dependability of the network to maintain redirects, so she asked me to put my own redirects away from obsolete URLS into the webapp.
I made a Controller called LegacyServletController to handle the obsolete URLs. It works great unless someone types a trailing slash onto the URL. The Controller method still picks it up, but it doesn't redirect to the new URL. It just adds the new URL to the URL already in the location bar.
For example, this is an obsolete URL:
http://blah.blah.blah/acme/moreinfo/
I would like it to redirect to
http://blah.blah.blah/acme/home
However, when the obsolete URL has a trailing slash as above, this is what the redirect produces:
http://blah.blah.blah/acme/moreinfo/home
I'm guessing I need another URL Handler in my *-servlet.xml, but I am still new to Spring and not sure how to set things up so my controller function handles obsolete URLs with and without trailing slashes properly.
Here is the controller class I am using to handle my legacy URLs
import org.springframework.stereotype.Controller;
import org.springframework.validation.*;
import org.springframework.ui.ModelMap;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.apache.log4j.Logger;
#Controller
public class LegacyServletController {
private static final Logger logger = Logger.getLogger(LegacyServletController.class);
// Redirect these legacy screns "home", the login screen via the logout process
#RequestMapping({"moreinfo","other_dead_screen"})
public String home() {
logger.debug("started...");
return "redirect:home";
}// end home()
}// end class LegacyServletController
Here is my acme-servlet.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.acme.controller" />
<mvc:resources mapping = "/**" location = "/,file:/apps1/bea/user_projects/domains/acme/common/,file:/c:/ftp/acme/"/>
<mvc:annotation-driven/>
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name = "prefix" value = "/jsp/"/>
<property name = "suffix" value = ".jsp"/>
</bean>
<bean name="af" class="com.acme.controller.security.CustomAuthenticationFilter"/>
</beans>
The return statement should be: return "redirect:/home". The controllers should return an absolute path for the view resolver.
The answers in this question are also helpful: redirect in Spring MVC
It would be better to provide a full path for the redirect, you can ideally return redirect:/home, this would redirect to a path relative to your web application root - http://blah.blah.blah/acme/home

Do not resolve view in RESTful Application

I am building a web application with RESTful web services using Spring MVC 3. The web services will be used by applications, so should never really resolve any requests to a view. Is there any way to specify in the servlet context that no requests should resolve to any view?
At the moment, I have:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
which I know tries to resolve any request to a correspondingly named view in the jsp folder. However, if I remove this, the application just tries to use a default view resolver.
The reason I am concerned about this is that my application logs are going to be full of the following messages (even though it works fine):
SEVERE: Servlet.service() for servlet [DispatcherServlet] in context with path [/vouchd] threw exception [Circular view path [signup]: would dispatch back to the current handler URL [/vouchd/signup] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)] with root cause
javax.servlet.ServletException: Circular view path [signup]: would dispatch back to the current handler URL [/vouchd/signup] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
or with the InternalViewResolver:
WARN [http-bio-8080-exec-4] (DispatcherServlet.java:1057) - No mapping found for HTTP request with URI [/app/WEB-INF/jsp/call.jsp] in DispatcherServlet with name 'DispatcherServlet'
which I guess is the better of the two evils. I don't want to turn off logging WARN level.
Try with #ResponseStatus. This code returns 204 with no content and view resolving skipped:
#ResponseStatus(NO_CONTENT)
void noView() {
//...
}
If you want to return raw data and simply serialize it to JSON or XML, use #ResponseBody:
#ResponseBody
MyPojo noView() {
return new MyPojo();
}