/**
* (C) Copyright 2013 Jabylon (http://www.jabylon.org) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
/**
*
*/
package org.jabylon.rest.ui.wicket;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import org.apache.wicket.Application;
import org.apache.wicket.ConverterLocator;
import org.apache.wicket.IConverterLocator;
import org.apache.wicket.Page;
import org.apache.wicket.ThreadContext;
import org.apache.wicket.application.AbstractClassResolver;
import org.apache.wicket.application.CompoundClassResolver;
import org.apache.wicket.authroles.authentication.AbstractAuthenticatedWebSession;
import org.apache.wicket.authroles.authentication.AuthenticatedWebApplication;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.protocol.http.WebSession;
import org.apache.wicket.request.IExceptionMapper;
import org.apache.wicket.request.IRequestMapper;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.Url;
import org.apache.wicket.request.http.WebRequest;
import org.apache.wicket.request.mapper.CompoundRequestMapper;
import org.apache.wicket.request.mapper.ICompoundRequestMapper;
import org.apache.wicket.util.IProvider;
import org.eclipse.emf.common.util.URI;
import org.jabylon.properties.PropertiesPackage;
import org.jabylon.rest.ui.Activator;
import org.jabylon.rest.ui.model.EMFFactoryConverter;
import org.jabylon.rest.ui.model.OSGiAwareBundleStringResourceLoader;
import org.jabylon.rest.ui.security.CDOAuthenticatedSession;
import org.jabylon.rest.ui.security.LoginPage;
import org.jabylon.rest.ui.security.PermissionBasedAuthorizationStrategy;
import org.jabylon.rest.ui.util.PageProvider;
import org.jabylon.rest.ui.wicket.components.AjaxFeedbackListener;
import org.jabylon.rest.ui.wicket.config.SettingsPage;
import org.jabylon.rest.ui.wicket.injector.OSGiInjector;
import org.jabylon.rest.ui.wicket.pages.ResourcePage;
import org.jabylon.rest.ui.wicket.pages.StartupPage;
import org.jabylon.rest.ui.wicket.pages.WelcomePage;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Johannes Utzig (jutzig.dev@googlemail.com)
*
*/
public class JabylonApplication extends AuthenticatedWebApplication {
@SuppressWarnings("rawtypes")
private ServiceTracker pageTracker;
private static Logger logger = LoggerFactory.getLogger(JabylonApplication.class);
/*
* (non-Javadoc)
*
* @see org.apache.wicket.Application#getHomePage()
*/
@Override
public Class<? extends Page> getHomePage() {
// if(startupPageMapped=)
// URIResolver connector = Activator.getDefault().getRepositoryLookup();
// if (connector != null)
return WelcomePage.class;
// return StartupPage.class;
}
@Override
public WebRequest newWebRequest(HttpServletRequest servletRequest, String filterPath) {
// https://github.com/jutzig/jabylon/issues/47
return new CustomWebRequest(servletRequest, filterPath);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
protected void init() {
super.init();
mount(new ResouceAwareMountedMapper("/", StartupPage.class)); //$NON-NLS-1$
getRequestCycleSettings().setResponseRequestEncoding("UTF-8");
getMarkupSettings().setDefaultMarkupEncoding("UTF-8");
OSGiInjector injector = new OSGiInjector(this);
getBehaviorInstantiationListeners().add(injector);
getResourceSettings().getStringResourceLoaders().add(new OSGiAwareBundleStringResourceLoader());
getApplicationSettings().setInternalErrorPage(CustomInternalErrorPage.class);
//first we look in our own classloader, then in the wicket one
CompoundClassResolver resolver = new CompoundClassResolver();
resolver.add(new AbstractClassResolver() {
@Override
public ClassLoader getClassLoader() {
return JabylonApplication.class.getClassLoader();
}
});
resolver.add(new AbstractClassResolver() {
@Override
public ClassLoader getClassLoader() {
return Application.class.getClassLoader();
}
});
getApplicationSettings().setClassResolver(resolver);
getComponentInstantiationListeners().add(injector);
getSecuritySettings().setAuthorizationStrategy(new PermissionBasedAuthorizationStrategy());
getAjaxRequestTargetListeners().add(new AjaxFeedbackListener());
final BundleContext bundleContext = Activator.getDefault().getContext();
pageTracker = new ServiceTracker(bundleContext, PageProvider.class, new ServiceTrackerCustomizer() {
@Override
public Object addingService(ServiceReference ref) {
PageProvider service = (PageProvider)bundleContext.getService(ref);
Object pathObject = ref.getProperty(PageProvider.MOUNT_PATH_PROPERTY);
if (pathObject instanceof String) {
String path = (String) pathObject;
Class pageClass = service.getPageClass();
if(pageClass==ResourcePage.class) {
//workaround so wicket doesn't choke because the thread context isn't filled (wrong thread)
ThreadContext.setApplication(JabylonApplication.this);
ThreadContext.setSession(new WebSession(createFakeRequest(null)));
//if the main page is ready, we can mount the rest of the pages
initMainPages();
}
logger.info("Mounting new page {} at {}", pageClass, path); //$NON-NLS-1$
mount(new ResouceAwareMountedMapper(path, pageClass));
} else {
logger.warn("Ignored Page {} because it was registered with invalid path property '{}'", service, pathObject); //$NON-NLS-1$
}
return service;
}
@Override
public void modifiedService(ServiceReference arg0, Object arg1) {
// TODO Auto-generated method stub
}
@Override
public void removedService(ServiceReference ref, Object service) {
Object pathObject = ref.getProperty(PageProvider.MOUNT_PATH_PROPERTY);
if (pathObject instanceof String) {
String path = (String) pathObject;
internalUmount(path);
}
}
});
pageTracker.open();
}
private void internalUmount(String path){
//workaround so wicket doesn't choke because the thread context isn't filled (wrong thread)
Application application = ThreadContext.getApplication();
if (application == null)
ThreadContext.setApplication(JabylonApplication.this);
if(ThreadContext.getSession()==null)
ThreadContext.setSession(new WebSession(createFakeRequest(null)));
// unmount(path);
/*
* umount seems to be greedy, e.g. a prefix match is enough.
* That's troublesome because umount /settings/log will also umount /settings
*/
ICompoundRequestMapper rootRequestMapperAsCompound = getRootRequestMapperAsCompound();
if (rootRequestMapperAsCompound instanceof CompoundRequestMapper) {
CompoundRequestMapper compound = (CompoundRequestMapper) rootRequestMapperAsCompound;
Iterator<IRequestMapper> it = compound.iterator();
while (it.hasNext()) {
IRequestMapper iRequestMapper = it.next();
if (iRequestMapper instanceof ResouceAwareMountedMapper) {
ResouceAwareMountedMapper mapper = (ResouceAwareMountedMapper) iRequestMapper;
if(path.equals(mapper.getMountPath()))
{
logger.info("Unmounting {}",path);
getRootRequestMapperAsCompound().remove(mapper);
}
}
}
}
}
private void initMainPages() {
unmount("/");
mount(new ResouceAwareMountedMapper("/login", LoginPage.class)); //$NON-NLS-1$
mount(new ResouceAwareMountedMapper("/settings", SettingsPage.class)); //$NON-NLS-1$
}
@Override
protected IConverterLocator newConverterLocator() {
ConverterLocator converterLocator = new ConverterLocator();
converterLocator.set(URI.class, new EMFFactoryConverter<URI>(PropertiesPackage.Literals.URI.getName()));
converterLocator.set(Locale.class, new EMFFactoryConverter<Locale>(PropertiesPackage.Literals.LOCALE.getName()));
return converterLocator;
}
@Override
protected Class<? extends AbstractAuthenticatedWebSession> getWebSessionClass() {
return CDOAuthenticatedSession.class;
}
@Override
protected Class<? extends WebPage> getSignInPageClass() {
return LoginPage.class;
}
@Override
public IProvider<IExceptionMapper> getExceptionMapperProvider()
{
return new CustomExceptionMapperProvider();
}
private static class CustomExceptionMapperProvider implements IProvider<IExceptionMapper>
{
@Override
public IExceptionMapper get()
{
return new CustomExceptionMapper();
}
}
Request createFakeRequest(final Url url)
{
return new Request()
{
@Override
public Url getUrl()
{
if(url==null)
return new Url();
return url;
}
@Override
public Locale getLocale()
{
return Locale.getDefault();
}
@Override
public Object getContainerRequest()
{
return null;
}
@Override
public Url getClientUrl()
{
return null;
}
@Override
public Charset getCharset()
{
return null;
}
};
}
}