6

I'm currently creating a set of links with code like this:

BookmarkablePageLink<CheeseMain> havarti =
    new BookmarkablePageLink<CheeseMain>("havarti", CheeseMain.class);
havarti.setParameter("Title", "Havarti");
havarti.setParameter("Group", "cheeseName");
add(havarti);

The URL that comes out has the format https://mysite.com/;jsessionid=B85EE5CB0349CCA2FE37AF76AB5C30C1?wicket:bookmarkablePage=:com.mycompany.cheese.CheeseMain&Title=Havarti&group=cheeseName.

My problem is that I no longer want the URL for this link to be bookmarkable. Ideally, I would like it to be something simple like https://mysite.com/cheese, but I can live with an ugly URL. The important thing is that the parameters aren't visible.

How should I change the way I'm generating my links? I've looked at the different URL encoding strategies that Wicket provides, but none of them remove the parameters; they just display them differently.

Pops
  • 30,199
  • 37
  • 136
  • 151
  • Omit the "Bookmarkable" bit, use a simple `PageLink`. If I remember correctly, it should work. – biziclop Jun 17 '11 at 20:47
  • @biziclop: I've been trying to use a standard `Link` -- actually, a subclass of `Link`, since `Link` is abstract -- but it involves a lot of other modifications. `PageLink` is deprecated, the Javadoc recommends using either `BookmarkablePageLink` or `Link` instead. – Pops Jun 17 '11 at 20:50
  • @Lord Torgamus Well, use a `Link` then. :) Or you can live with it being deprecated and use `PageLink`. Although I don't see how using a `Link` can be much more complicated. Can you add some more detail about this? Maybe we can work out a simple solution with `Link`s – biziclop Jun 17 '11 at 20:53
  • @biziclop: Well, `Link` doesn't offer a `setParameter()` method, so I resorted to using a `PageParameters` object. But that requires a new constructor for the destination page. I'm having trouble understanding the session/request handling in the destination page, but that's getting beyond the scope of this question. – Pops Jun 17 '11 at 20:57
  • 1
    As far as I know, passing a `PageParameters` instance to the `setResponsePage` method and calling `BookmarkablePageLink.setParameter()` has the same effect. – biziclop Jun 17 '11 at 21:24
  • @Lord Torgamus You should use the super constructor `Page(PageParameters params)` regardless. It is used in the `Page` class to store such parameters. If you just use the empty constructor, subsequent calls to `Page#getPageParameters()` will return null, even if you used `BookmarkablePageLink#setParameter()`. You may not have encountered this problem yet, but you will... – jbrookover Jun 18 '11 at 13:43
  • What worries me about `PageParameters` is that _Wicket in Action_ says "A page is considered bookmarkable if ... it has a constructor that takes a `PageParameters` instance as the only argument." @biziclop – Pops Jun 20 '11 at 14:03
  • @jbrookover, that's not _too_ critical an issue for me, as that page's parameters are currently coming in via a [different path](http://www.jarvana.com/jarvana/view/org/apache/wicket/wicket/1.4.1/wicket-1.4.1-javadoc.jar!/org/apache/wicket/protocol/http/servlet/ServletWebRequest.html). – Pops Jun 20 '11 at 14:05

2 Answers2

8

The parameters appear in the URL only if the page is bookmarkable, or the specific link is bookmarkable.

If you create a Link that navigates to the page using setResponsePage(Page) (passing a Page instance) instead of setResponsePage(Class<Page>, PageParameters) (passing a Page class), the link created will not point to the bookmarkable version of the page, but to a stateful instance.

To make this work, though, you must not call the super(PageParameters) constructor (so that the Page doesn't have enough information to build the stateless URL).

In this example, you can navigate to the SecretPage through two different links, one stateless, bookmarkable, and the other stateful.

SecretPage also has two constructors. One receives a PageParameters and calls super passing it. The other receives the value directly via construcor parameter, and doesn't pass it to super (if it'd called super(new PageParameters().add("message",message), as in the commented line, it would automatically redirect to a bookmarkable URL).

HomePage.java:

public class HomePage extends WebPage {
    public HomePage(final PageParameters parameters) {
        add(new BookmarkablePageLink<Void>("bookmarkable", SecretPage.class,
            new PageParameters().add("message", "This message will appear in the URL")));
        add(new Link<Void>("instance") {
            @Override
            public void onClick() {
                setResponsePage(new SecretPage("This message will NOT appear in the URL"));
            }
        });
    }
}

HomePage.html:

<html xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd" >
<body>
  <p><a wicket:id="bookmarkable">Bookmarkable link (stateless)</a></p>
  <p><a wicket:id="instance">Hidden parameters link (stateful)</a></p>
</body>
</html>

SecretPage.java

public class SecretPage extends WebPage {
    public SecretPage(PageParameters parameters) {
        super(parameters);
        init(parameters.get("message").toString("No message!"));
    }
    public SecretPage(String message) {
        // super(new PageParameters().add("message", message)); // COMMENTED!
        init(message);
    }
    private void init(String message) {
        info(message);
        add(new FeedbackPanel("feedback"));
        add(new BookmarkablePageLink<Void>("back", getApplication().getHomePage()));
    }
}

SecretPage.html

<html xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd" >
<body>
  <p wicket:id="feedback"></p>
  <p><a wicket:id="back">BACK</a></p>
</body>
</html>

And, to have a simple URL, like http://host/app/secret, you must mount it. You can do it in your WebApplication class.

WicketApplication.java

public class WicketApplication extends WebApplication {
    @Override
    protected void init() {
        super.init();
        mountPage("home", getHomePage());
        mountPage("secret", SecretPage.class);
    }
    public Class<HomePage> getHomePage() {
        return HomePage.class;
    }
}

This example uses Wicket 1.5 (still RC4.2), and need some modifications to work with 1.4.x (some methods and classes were renamed, or moved to different packages), but the idea is the same.

tetsuo
  • 10,726
  • 3
  • 32
  • 35
  • I am in fact using Wicket 1.4.1, so I had to convert this code. It wasn't too difficult, but I was puzzled by the last line of the first `SecretPage` constructor. Why does it give an argument to `toString()`? That won't even compile, for me. – Pops Jun 20 '11 at 20:05
  • The API changed in 1.5. In 1.4, the equivalent would be something like `parameters.getString("message", "No message!")` – tetsuo Jun 20 '11 at 20:55
  • Oh, wow, an intentional parameter for `toString()`. I didn't even consider that; I figured it was a typo. With that fix, this does appear to work as described. Now, to figure out how to modify it for my project.... – Pops Jun 20 '11 at 21:18
  • @tetsuo: I posted a [somewhat related question](http://stackoverflow.com/questions/10189570/passing-non-query-string-parameters-in-pageparameters); I was wondering if you'd be able to answer that too... :-) – Jonik Apr 17 '12 at 10:37
0

You could still use your URL encoding strategy of choice and encapsulate your parameters into an inherited class such as:

public class Havarti extends CheeseMain {
    public Havarti() {
        super(new PageParameters("Title=Havarti,Group=cheeseName"));
    }
}

Then you can use: new BookmarkablePageLink<Void>("havarti", Havarti.class).

Marcelo
  • 11,218
  • 1
  • 37
  • 51
  • With this strategy, the URL would still be giving up information. It would also involve creating hundreds of new classes to replace a page that really should just take a parameter and call the DB with it. (My code there is just an example; I'm not really building a cheese app.) – Pops Jun 17 '11 at 20:09
  • @Lord That is the only way to do it without *showing* the parameters. The other thing you can do is encrypt the parameters when building the URL and decrypt them in your constructor (The encrypted values would still be showing though). I don't really know if you are doing a cheese app xD, I just wanted my code to relate to yours. – Marcelo Jun 17 '11 at 20:33