delete version number in url - url

How can I delete or hide the version number in the URL introduced in Wicket 1.5?
Mounting a page doesn't help.
http://localhost/MyPage/SubPage?0

In Application.init():
mount(new MountedMapperWithoutPageComponentInfo("/subpage", MyPage.class));
with the following Mapper class:
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) {
// do nothing so that component info does not get rendered in url
}
#Override
public Url mapHandler(IRequestHandler requestHandler)
{
if (requestHandler instanceof ListenerInterfaceRequestHandler ||
requestHandler instanceof BookmarkableListenerInterfaceRequestHandler) {
return null;
} else {
return super.mapHandler(requestHandler);
}
}
}

If you don't want the version number then you page should be completely stateless, the version number is meant for stateful pages. For instance if your page includes a form then you should use the stateless variant of the Form component, that is org.apache.wicket.markup.html.form.StatelessForm.
If your page is already completely stateless, you can give wicket a hint by invoking the org.apache.wicket.Page#setStatelessHint method.

The solution using a self-created MountedMapperWithoutPageComponentInfo class doesn't work for Wicket 6.13+, the page won't respond to callback user actions. (Note that there are multiple versions of MountedMapperWithoutPageComponentInfo on the Internet.)
A solution for 6.13+ (tested with 6.15) can be found here:
http://apache-wicket.1842946.n4.nabble.com/Delete-version-number-in-url-td4665752.html
https://svn.apache.org/repos/asf/openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
// Put this code in your WebApplication subclass
import org.apache.wicket.core.request.mapper.MountedMapper;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.request.mapper.parameter.PageParametersEncoder;
import org.apache.wicket.request.mapper.info.PageComponentInfo;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.core.request.handler.BookmarkableListenerInterfaceRequestHandler;
private static class NoVersionMapper extends MountedMapper {
public NoVersionMapper(final Class<? extends IRequestablePage> pageClass) {
this("/", pageClass);
}
public NoVersionMapper(String mountPath, final Class<? extends IRequestablePage> pageClass) {
super(mountPath, pageClass, new PageParametersEncoder());
}
#Override
protected void encodePageComponentInfo(Url url, PageComponentInfo info) {
//Does nothing
}
#Override
public Url mapHandler(IRequestHandler requestHandler) {
if (requestHandler instanceof ListenerInterfaceRequestHandler || requestHandler instanceof BookmarkableListenerInterfaceRequestHandler) {
return null;
} else {
return super.mapHandler(requestHandler);
}
}
}
Then you can mount pages using:
// Put this in the init() method of your WebApplication subclass
getRootRequestMapperAsCompound().add(new NoVersionMapper("urlPatternOfAPage", YourPage.class));

Use the following mapper to mount pages, this should work on any book markable page except the homepage.
Here's how to use the mapper in Application.init()
mount(new MountedMapperWithoutPageComponentInfo("/subpage", MyPage.class));
Here's the mapper.
import org.apache.wicket.request.Url;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.request.mapper.MountedMapper;
import org.apache.wicket.request.mapper.info.PageComponentInfo;
import org.apache.wicket.request.mapper.parameter.PageParametersEncoder;
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) {
// does nothing so that component info does not get rendered in url
}
}

For me the solution with setStatelessHint didn't work. The following did work:
class MyApplication extends WebApplication {
#Override protected void init() {
getRequestCycleSettings().setRenderStrategy(
IRequestCycleSettings.RenderStrategy.ONE_PASS_RENDER);
....
}
}

For Wicket 8, this NoVersionMapper class works:
https://github.com/apache/openmeetings/blob/master/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java#L314
public class NoVersionMapper extends MountedMapper {
public NoVersionMapper(final Class pageClass) {
this("/", pageClass);
}
public NoVersionMapper(String mountPath, final Class pageClass) {
super(mountPath, pageClass, new PageParametersEncoder());
}
#Override
protected void encodePageComponentInfo(Url url, PageComponentInfo info) {
//Does nothing
}
#Override
public Url mapHandler(IRequestHandler requestHandler) {
if (requestHandler instanceof ListenerRequestHandler || requestHandler instanceof BookmarkableListenerRequestHandler) {
return null;
} else {
return super.mapHandler(requestHandler);
}
}
}
This is basically the same as Devabc's code but this one compiles on Wicket 8. It has been tested against known regressions of the previous versions of the code: Ajax works and no page refreshing is triggered when it shouldn't.

The workarounds suggested so far may work with specific releases and have side effects. They should be considered hacks. I have used these hacks and they were broken by new releases. Therefore I have created a request for generic framework support here (please comment / vote): setVersioned(false) should force single Page Version.
Another example of a side effect: Page Reload on Submit of non-versioned Page

Related

Run code in Vaadin 8 application idependent of UI

In earlier versions, you could have a class which implements ServletContextListener and put your code in the contextInitialized method, so that it runs when the server starts. This is useful for loading up the database into memory. How does one achieve this in a Vaadin 8 project?
In exactly the same way: By registering a ServletContextListener. You can use the #WebListener annotation for this. For example:
public class WebConfig {
#WebServlet("/*")
#VaadinServletConfiguration(ui = VaadinUI.class, productionMode = false)
public static class JdbcExampleVaadinServlet extends VaadinServlet {
}
#WebListener
public static class JdbcExampleContextListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent sce) {
try {
DatabaseService.init();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
DatabaseService.shutdown();
}
}
}

How to inject a bean into custom argument resolver?

Hello i use spring boot 1.3.2 version. I have a custom argument resolver which's name is ActiveCustomerArgumentResolver. Everything is great, resolveArgument method works fine but i can't initialize my service component which is of my custom arg. resolver. Is there a problem with lifecycle process? Here is my code:
import org.springframework.beans.factory.annotation.Autowired;
//other import statements
public class ActiveCustomerArgumentResolver implements HandlerMethodArgumentResolver {
#Autowired
private CustomerService customerService;
#Override
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(ActiveCustomer.class) && parameter.getParameterType().equals(Customer.class))
return true;
else
return false;
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Principal userPrincipal = webRequest.getUserPrincipal();
if (userPrincipal != null) {
Long customerId = Long.parseLong(userPrincipal.getName());
return customerService.getCustomerById(customerId).orNull(); //customerService is still NULL here, it keeps me getting NullPointerEx.
} else {
throw new IllegalArgumentException("No user principal is associated with the current request, yet parameter is annotated with #ActiveUser");
}
}
}
Let the Spring create the resolver for you by making it a Component:
#Component
public class ActiveCustomerArgumentResolver implements HandlerMethodArgumentResolver {...}
Then inject the resolver into your WebConfig instead of simply using the new, like following:
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired private ActiveCustomerArgumentResolver activeCustomerArgumentResolver;
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(activeCustomerArgumentResolver);
}
}
This is how i've solved the problem, not a generic one but helps me a lot:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends WebMvcConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(activeCustomerArgumentResolver());
}
#Bean
public ActiveCustomerArgumentResolver activeCustomerArgumentResolver() {
return new ActiveCustomerArgumentResolver();
}
}

Grails Spring Security Get Roles for the Current Page

I was wondering if anyone knows an elegant way to get all the roles in the spring security plugin that have access to the current page.
I am using spring security and it's configured to use RequestMap domain objects.
The permissions in my app are pretty complex so I wanted to make a tag at the bottom of each page displaying the roles need to use the page.
I was doing a query for the request map but I want to make sure the way I match the url is the same as the way the plugin does.
Ideally I wouldn't have to run a query at all.
Grails version 2.2.1 Spring Security Plugin version 1.2.7.3
Thanks in advance
I got this to work by adding the following two classes to my src/java.
Class 1
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
FilterInvocationSecurityMetadataSource oldBean;
#Override
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
FilterInvocation filterInvocation = (FilterInvocation) o;
HttpServletRequest request = filterInvocation.getHttpRequest();
request.setAttribute("PAGEROLES", oldBean.getAttributes(filterInvocation));
return oldBean.getAttributes(o);
}
#Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return oldBean.getAllConfigAttributes();
}
#Override
public boolean supports(Class<?> aClass) {
return FilterInvocation.class.isAssignableFrom(aClass);
}
public Object getOldBean() { return oldBean; }
public void setOldBean(FilterInvocationSecurityMetadataSource oldBean) { this.oldBean = oldBean; }
}
Class 2
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
public class FilterSecurityMDSExtractor implements BeanPostProcessor, BeanFactoryAware {
private ConfigurableListableBeanFactory bf;
private FilterInvocationSecurityMetadataSource metadataSource = new MyFilterInvocationSecurityMetadataSource();
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof FilterInvocationSecurityMetadataSource) {
((MyFilterInvocationSecurityMetadataSource) metadataSource).setOldBean((FilterInvocationSecurityMetadataSource) bean);
return metadataSource;
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.bf = (ConfigurableListableBeanFactory)beanFactory;
}
}
I then added the following to resources.groovy
beans = {
filterSecurityMDSExtractor(FilterSecurityMDSExtractor)
}
Basically I am stuffing the user roles into the request
request.setAttribute("PAGEROLES", oldBean.getAttributes(filterInvocation));
then all I have to do is call the following
request.getAttribute("PAGEROLES");
to get the roles back out. I pieced together my solution by stealing from other great posts on Stackoverflow. Someone else might have a better solution but so far this is working for me.

How to explicitly access the Connector from Widget Side into Vaadin 7?

I create a Widget with his Server Side Class and the Client Side (Connector Class, ServerRPC Class, State Class and Widget Class).
Connector :
#Connect(Custom.class)
public class CustomConnector extends ButtonConnector {
...
public void myFunc() {
// DO Something
}
}
Widget :
public class CustomWidget extends VButton {
...
private CustomConnector conn = new CustomConnector();
public CustomWidget () {
conn.myFunc();
}
...
}
Now from the Widget Class i want to explicitly call/access the Connector Object, which are not a Singleton, so that i can access a function too. How can i solve it?
In my opinion you should not access connector directly from GWT widget. It is against Vaadin 7 architecture where GWT widgets are objects independent from vaadin at all.
However if we are talking about dirty migration from Vaadin 6 to 7 solution could be:
ComponentConnector connector = ConnectorMap.get(client).getConnector(CustomWidget.this); // client is taken from updateFromUIDL method (Vaadin6)
Better solution will be to add "state" listener to the widget
public interface CustomWidgetStateListener {
public void stateChanged();
}
public class CustomWidget extends VButton {
...
CustomWidgetStateListener listener;
public void addStateListener(CustomWidgetStateListener listener) {
this.listener = listener;
}
public void notifyStateChanged() { // You can call notifyStateChanged() whenever you want to notify connector
listener.stateChanged();
}
...
}
public class CustomConnector extends ButtonConnector {
public CustomConnector() {
getWidget().addStateListener(new CustomWidgetStateListener() {
public void stateChanged() {
myFunc();
}
});
}
...
public void myFunc() {
// DO Something
}
}

How Google Guice is working without #Inject anywhere in my test program?

I started exploring Google Guice today to do dependency injection in my application.
I noticed I am not using #Inject annotation anywhere. But it's working. Can not understand this concept. In this example, where #Inject is the best fit in my scenerio? If any one can point me out.
public interface Tweeter {
public void sendTweet(String message);
}
public class SmsTweeter implements Tweeter {
#Override
public void sendTweet(String message) {
System.out.println("You SMS tweet: "+message);
}
}
import com.google.inject.AbstractModule;
public class TweetModule extends AbstractModule{
#Override
protected void configure() {
bind(Tweeter.class).to(SmsTweeter.class);
}
}
import com.google.inject.Guice;
import com.google.inject.Injector;
public class TestTweetClient {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new TweetModule());
Tweeter tweeter = injector.getInstance(Tweeter.class);
tweeter.sendTweet("Hi there");
}
}
It prints (the hidden implementation works):
You SMS tweet: Hi there
There is no best fit for #Inject in your example. The class SmsTweeter has an implicit zero-args constructor. You could make it explicit and add #Inject there but it is not necessary.
public class SmsTweeter implements Tweeter {
#Inject
SmsTweeter() {
// nothing to do
}
#Override
public void sendTweet(String message) {
System.out.println("You SMS tweet: "+message);
}
}

Resources