GWT tokenizer: How to change URL - gwt

I am using Activities and Places.
I have a LoginPlace.
The url displayed when I navigate to that place has this at the end:
#LoginPlace:login
How can I change this to just #login or something else?
My tokenizer looks like this:
public class LoginTokenizer implements PlaceTokenizer<LoginPlace> {
private LoginPlace loginPlace;
public LoginTokenizer() {
}
#Override
public LoginPlace getPlace(String token) {
return new LoginPlace(token);
}
#Override
public String getToken(LoginPlace place) {
loginPlace = place;
return loginPlace.getLoginToken();
}
}
And navigation to the LoginPlace is done through the PlaceController:
clientFactory.getPlaceController().goTo(new LoginPlace("login"));
Where can I manipulate the format of the URL?

The mapping is done by the PlaceHistoryMapper.
You can have an implementation generated by GWT based in PlaceTokenizers, but then it's based on a prefix/suffix. The #Prefix allows you configure the prefix (which otherwise defaults to the place class' name).
Or you can implement the interface yourself and have complete control over the process.

Rename the Place class from LoginPlace to Login.
Pass an empty token:
new LoginPlace("")

Related

Unable to fetch OSGi Configuration Values

I have created an event handler and used OSGi configuration as below.
#Component(immediate = true,
service=EventHandler.class,
property= {
EventConstants.EVENT_TOPIC + "=" + ReplicationAction.EVENT_TOPIC
}
)
#Designate(ocd = PagePublishEventHandler.Configuration.class)
public class PagePublishEventHandler implements EventHandler {
private static String rootPage = "";
#Override
public void handleEvent(final Event event) {
}
#Activate
#Modified
public void activate(Configuration config) {
String rootPage = config.getPath();
logger.info("********ConfigurationPropertyInterface**********activate**********************");
logger.info("********rootPage********",rootPage);
}
#ObjectClassDefinition(name="AEM Plugin OSGi Configuration")
public #interface Configuration {
#AttributeDefinition(
name = "Root Page For Web Site",
description = "Configurable paths for root page",
type = AttributeType.STRING
)
String getPath() default "/content";
}
}
Inside the activate method, Value of rootPage is always blank. Do anyone has the solution on this.
I
Thanks
I ran your code in my machine and I don't see any issues with it except for the following things which I noticed.
The rootPage is defined as a static variable. Though this is not the cause of the issue in question, it might cause issues during runtime.
You are not printing the value of the rootPage in your log (probably that is why you think that the value is null?). In order to print it, use format specifiers as shown below.
logger.info("********rootPage******** {}",rootPage);

Using HeaderResponseContainer: No FilteringHeaderResponse is present in the request cycle

I'm trying to add a custom HeaderResponseContainer in my wicket application. The tutorial looks quite simple (see Positioning of contributions), but when I add these lines and run the application I alwas get an IllegalStateException:
java.lang.IllegalStateException: No FilteringHeaderResponse is present in the request cycle. This may mean that you have not decorated the header response with a FilteringHeaderResponse. Simply calling the FilteringHeaderResponse constructor sets itself on the request cycle
at org.apache.wicket.markup.head.filter.FilteringHeaderResponse.get(FilteringHeaderResponse.java:165)
at org.apache.wicket.markup.head.filter.HeaderResponseContainer.onComponentTagBody(HeaderResponseContainer.java:64)
at org.apache.wicket.markup.html.panel.DefaultMarkupSourcingStrategy.onComponentTagBody(DefaultMarkupSourcingStrategy.java:71)
...
Yes, I already saw the note about FilteringHeaderResponse. But I am not sure where I should call the constructor. I already tried to add it in renderHead before calling response.render but I still get the same exception:
public void renderHead(IHeaderResponse response) {
super.renderHead(response);
FilteringHeaderResponse resp = new FilteringHeaderResponse(response);
resp.render(new FilteredHeaderItem(..., "myKey"));
}
You can create a decorator that wraps responses in a FilteringHeaderResponse:
public final class FilteringHeaderResponseDecorator implements IHeaderResponseDecorator {
#Override
public IHeaderResponse decorate(IHeaderResponse response) {
return new FilteringHeaderResponse(response);
}
}
And that set it during application initialization:
Override
public void init() {
super.init();
setHeaderResponseDecorator(new FilteringHeaderResponseDecorator());
}
I just ran into this same problem and found that the Wicket In Action tutorial leaves out the part about setting up a custom IHeaderResponseDecorator in your main Wicket Application init. The Wicket guide has a more thorough example:
Apache Wicket User Guide - Put JavaScript inside page body
You need something like this in your wicket Application:
#Override
public void init()
{
setHeaderResponseDecorator(new JavaScriptToBucketResponseDecorator("myKey"));
}
/**
* Decorates an original IHeaderResponse and renders all javascript items
* (JavaScriptHeaderItem), to a specific container in the page.
*/
static class JavaScriptToBucketResponseDecorator implements IHeaderResponseDecorator
{
private String bucketName;
public JavaScriptToBucketResponseDecorator(String bucketName) {
this.bucketName = bucketName;
}
#Override
public IHeaderResponse decorate(IHeaderResponse response) {
return new JavaScriptFilteredIntoFooterHeaderResponse(response, bucketName);
}
}

Apache Wicket bookmarkable url added one additional parameter to the link, why?

my map is
mountPage("/page/#{code}/#{name}", Page.class);
but when I click on the link
localhost/page/10/toy?2
wicket add also one parameter like a counter, when I refresh the page I have
localhost/page/10/toy?3
why?
This is because your page are stateful, Wicket manages its own states to your page by appending this "counter". This way, when your user navigate backward using its browser built-in functionnality, the page is displayed has it has been previously.
If you don't want such a parameter in your URL, you'll need to dig out and eradicate every stateful component in your pages.
You can create
public class MountedMapperWithoutPageComponentInfo extends MountedMapper {
public MountedMapperWithoutPageComponentInfo(String mountPath, Class<? extends IRequestablePage> pageClass) {
super(mountPath, pageClass, new PageParametersEncoder());
}
#Override
protected void encodePageComponentInfo(Url url, PageComponentInfo info) {
}
#Override
public Url mapHandler(IRequestHandler requestHandler) {
if (requestHandler instanceof ListenerInterfaceRequestHandler) {
return null;
} else {
return super.mapHandler(requestHandler);
}
}
}
and map page on Application class like this
mount(new MountedMapperWithoutPageComponentInfo("/page/#{code}/#{name}", Page.class));

How to find out component-path

I use junit to assert the existing of wicket components:
wicketTester.assertComponent("dev1WicketId:dev2WicketId:formWicketId", Form.class);
This works for some forms. For complex structure, it is defficult to find out the path of the form by searching all html files. Is there any method how to find out the path easy?
If you have the component you can call #getPageRelativePath(). E.g.
// Supposing c is a component that has been added to the page.
// Returns the full path to the component relative to the page, e.g., "path:to:label"
String pathToComponent = c.getPageRelativePath();
You can get the children of a markup container by using the visitChildren() method. The following example shows how to get all the Forms from a page.
List<Form> list = new ArrayList<Form<?>>();
Page page = wicketTester.getLastRenderedPage();
for (Form form : page.visitChildren(Form.class)) {
list.add(form);
}
An easy way to get those is to call getDebugSettings().setOutputComponentPath(true); when initializing your application. This will make Wicket to output these paths to the generated HTML as an attribute on every component-bound tag.
It's recommended to only enable this on debug mode, though:
public class WicketApplication extends WebApplication {
#Override
public void init() {
super.init();
if (getConfigurationType() == RuntimeConfigurationType.DEVELOPMENT) {
getDebugSettings().setOutputComponentPath(true);
}
}
}
Extending the RJo's answer.
It seems that the method page.visitChildren(<Class>) is deprecated (Wicket 6), so with an IVisitor it can be :
protected String findPathComponentOnLastRenderedPage(final String idComponent) {
final Page page = wicketTester.getLastRenderedPage();
return page.visitChildren(Component.class, new IVisitor<Component, String>() {
#Override
public void component(final Component component, final IVisit<String> visit) {
if (component.getId().equals(idComponent)) {
visit.stop(component.getPageRelativePath());
}
}
});
}

Place name without a colon (:)?

Still a bit of a GWT noob here but making progress using Activities and Places as described by Google here.
I understand that a Place's "URL consists of the Place's simple class name (like "HelloPlace") followed by a colon (:) and the token returned by the PlaceTokenizer.
Can I somehow remove the colon when I don't have a token to send?
For example, I'm fine with a URL like this "#editPerson:2" when I need to work with PersonId=2. But what about when I just want to present a blank Person form? In that case I would prefer to use "#addPersonForm" rather than "#addPersonForm:"
Any suggestions (even better code suggestions) would be most appreciated!
You can provide your own PlaceHistoryMapper (without using the generator) as already suggested by Boris_siroB, or you can do it within a PlaceTokenizer with an empty prefix: with an empty prefix, there won't be a colon, and the tokenizer can do whatever you want. If you totally distinct places, make it a tokenizer of Place, so it's also the "catchall" for getToken. That way you can keep all the advantages of the generation with prefixes, PlaceTokenizers and WithTokenizers (if you want to take advantage of them)
To take full control of the URL hash (that is to generate your own tokens from Places and map these tokens back to Places) you can implement your own history mapper (a class implementing the PlaceHistoryMapper interface).
public class MyPlaceHistoryMapper implements PlaceHistoryMapper {
#Override
public Place getPlace(String token) {
// parse tokens and create Places here
}
#Override
public String getToken(Place place) {
// examine Places and compose tokens here
}
}
In your entry point class you'd then replace the line:
AppPlaceHistoryMapper historyMapper = GWT.create(AppPlaceHistoryMapper.class);
with:
PlaceHistoryMapper appHistoryMapper = new MyPlaceHistoryMapper();
That's it. Your URL hashes no longer need to be class name-based or to use the : delimiter.
I'm using a PlaceHistoryMapper decorator named PlaceHistoryMapperWithoutColon.
Usage :
final PlaceHistoryMapper historyMapper0 = GWT
.create(PlaceHistoryMapperImpl.class);
final PlaceHistoryMapper historyMapper = new PlaceHistoryMapperWithoutColon(historyMapper0);
Decorator source :
public class PlaceHistoryMapperWithoutColon implements PlaceHistoryMapper {
private static final String COLON = ":";
private PlaceHistoryMapper placeHistoryMapper;
public PlaceHistoryMapperWithoutColon(PlaceHistoryMapper placeHistoryMapper) {
this.placeHistoryMapper = placeHistoryMapper;
}
#Override
public Place getPlace(String token) {
if (token != null && !token.endsWith(COLON)) {
token = token.concat(COLON);
}
return placeHistoryMapper.getPlace(token);
}
#Override
public String getToken(Place place) {
String token = placeHistoryMapper.getToken(place);
if (token != null && token.endsWith(COLON)) {
token = token.substring(0, token.length() - 1);
}
return token;
}
}
Decorated source example :
#WithTokenizers({ FirstPlace.Tokenizer.class, SecondPlace.Tokenizer.class })
public interface PlaceHistoryMapperImpl extends PlaceHistoryMapper {
}
Place source example :
public final class FirstPlace extends Place {
#Prefix("first")
public static class Tokenizer implements PlaceTokenizer<FirstPlace> {
#Override
public NetworkInfosPlace getPlace(String token) {
return new FirstPlace ();
}
#Override
public String getToken(FirstPlace place) {
return "";
}
}
}