0

I have my application url which I am sending the the end user on their emails.

Now that url contains the 'username' field, which can contains '@' character.

For e.g. link which sent to the end user :

http://localhost:8080/my-app/someaction/activateuser/abc@def.com/somedata/

Now whenever user clicks on above link, its throwing following exception :

java.lang.IllegalArgumentException
    Input string 'abc@def.com' is not valid; the character '@' at position 4 is not valid.
    at org.apache.tapestry5.internal.services.URLEncoderImpl.decode(URLEncoderImpl.java:144)
    at $URLEncoder_137022607d9.decode($URLEncoder_137022607d9.java)
    at org.apache.tapestry5.internal.services.ContextPathEncoderImpl.decodePath(ContextPathEncoderImpl.java:92)
    at $ContextPathEncoder_137022607cd.decodePath($ContextPathEncoder_137022607cd.java)
    at org.apache.tapestry5.internal.services.ComponentEventLinkEncoderImpl.checkIfPage(ComponentEventLinkEncoderImpl.java:328)
    at org.apache.tapestry5.internal.services.ComponentEventLinkEncoderImpl.decodePageRenderRequest(ComponentEventLinkEncoderImpl.java:307)
    at org.apache.tapestry5.internal.services.linktransform.LinkTransformerInterceptor.decodePageRenderRequest(LinkTransformerInterceptor.java:68)
    at $ComponentEventLinkEncoder_137022607c1.decodePageRenderRequest($ComponentEventLinkEncoder_137022607c1.java)
    at org.apache.tapestry5.internal.services.PageRenderDispatcher.dispatch(PageRenderDispatcher.java:41)
    at $Dispatcher_137022607c2.dispatch($Dispatcher_137022607c2.java)
    at $Dispatcher_137022607bd.dispatch($Dispatcher_137022607bd.java)
    at org.apache.tapestry5.services.TapestryModule$RequestHandlerTerminator.service(TapestryModule.java:321)
    at org.apache.tapestry5.internal.services.RequestErrorFilter.service(RequestErrorFilter.java:26)

Is there any way to handle such scenario, like encoding/decoding the urls ?

Nirmal
  • 4,789
  • 13
  • 72
  • 114
  • How do you generate the url? It sounds like you go around the normal tapestry way for generating a url as, if I'm not mistaken, Tapestry does all the encoding for you. – joostschouten Apr 30 '12 at 11:27

3 Answers3

2

You cannot have an @ in the url, because it's a reserved character (the specific RFC is RFC 3986).

You can use the URLEncoder class to encode the url to an acceptable value

miniBill
  • 1,743
  • 17
  • 41
  • Currently, Tapestry uses its own idiosyncractic URLEncoder; this came about as a way to normalize behavior across servlet containers. This is something that needs to be revisited. – Howard M. Lewis Ship Apr 30 '12 at 20:16
  • or just duplicated in Tapestry.js somehow Howard? it would solve many issues if it could be replicated on the client... – pstanton May 01 '12 at 05:51
1

As MiniBill has already answered, that can't work, and as Howard has added, Tapestry has its own encoder for URLs. This means that the easiest way for you to get a URL in the format that Tapestry can read is to have Tapestry create it, and then pass it to the component that sends your emails:

@Inject
private LinkSource linkSource;

@OnEvent(...)
void sendActivationEmail() {
    final Link activationLink = this.createUserActivationLink(email, otherStuff);
    this.activationEmailSender.sendWithActivationLink(email, activationLink);
}

private Link createUserActivationLink(String email, String otherStuff) {
    return linkSource.createPageRenderLink(
       "someaction/activateuser", false, email, otherStuff);
}
Henning
  • 16,063
  • 3
  • 51
  • 65
0

I was able to solve the problem by encoding my string to Base64, and unpacking on Tapestry Java side. My strings were of UTF-8 encoded characters.

I modified the Base64 encoder from this answer: https://stackoverflow.com/a/40392850/5339857

function b64EncodeUnicode(str) {
    return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
        return String.fromCharCode('0x' + p1);
    })).replace(/\=+$/, '');
}

(just added the .replace in the end, to remove padding =s that Tapestry doesn't like)

And in the Java side the decoding was a breeze: (this example is of an ajax click from javascript - where the Base64 encoding happens)

@OnEvent(value = "clickAjax")
Object clickAjax(String parameter) {
    somePagePropetry = new String(java.util.Base64.getDecoder().decode(parameter));
    return this;
}
Community
  • 1
  • 1
Roy Shilkrot
  • 3,079
  • 29
  • 25