package fr.openwide.core.wicket.more.notification.service;
import java.util.Locale;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.wicket.Application;
import org.apache.wicket.Session;
import org.apache.wicket.ThreadContext;
import org.apache.wicket.protocol.http.BufferedWebResponse;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.protocol.http.mock.MockHttpServletRequest;
import org.apache.wicket.protocol.http.mock.MockHttpServletResponse;
import org.apache.wicket.protocol.http.mock.MockHttpSession;
import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
import org.apache.wicket.protocol.http.servlet.ServletWebResponse;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.http.WebResponse;
import org.springframework.beans.factory.annotation.Autowired;
import com.google.common.base.MoreObjects;
import fr.openwide.core.commons.util.context.AbstractExecutionContext;
import fr.openwide.core.commons.util.context.ExecutionContexts;
import fr.openwide.core.commons.util.context.IExecutionContext;
import fr.openwide.core.spring.property.service.IPropertyService;
import fr.openwide.core.wicket.more.property.WicketMorePropertyIds;
public class WicketContextProviderImpl implements IWicketContextProvider {
@Autowired
private IPropertyService propertyService;
private final WebApplication defaultApplication;
private final RequestCycleExecutionContext requestCycleExecutionContext = new RequestCycleExecutionContext();
/**
* @param defaultApplication
* The the wicket application to be used by contexts when none was provided by the client.
*/
public WicketContextProviderImpl(WebApplication defaultApplication) {
this.defaultApplication = defaultApplication;
}
@Override
public IExecutionContext context() {
return context(null, null);
}
@Override
public IExecutionContext context(Locale locale) {
return context(null, locale);
}
@Override
public IExecutionContext context(WebApplication application) {
return context(application, null);
}
@Override
public IExecutionContext context(WebApplication application, Locale locale) {
Locale actualLocale = locale == null ? null : propertyService.toAvailableLocale(locale);
return ExecutionContexts.composite()
.add(new ApplicationExecutionContext(application))
.add(requestCycleExecutionContext)
.add(new SessionExecutionContext(actualLocale))
.build();
}
private final class ApplicationExecutionContext extends AbstractExecutionContext {
private final WebApplication application;
public ApplicationExecutionContext(WebApplication application) {
super();
this.application = application;
}
@Override
public ITearDownHandle open() {
final ThreadContext initialContext = ThreadContext.get(false);
Application currentApplication = ThreadContext.getApplication();
if (currentApplication != null
&& (application == null || currentApplication == application)) {
return ExecutionContexts.noOp().open();
}
ITearDownHandle handle = new ITearDownHandle() {
@Override
public void close() {
ThreadContext.restore(initialContext);
}
};
WebApplication targetApplication = getTargetApplication();
try {
ThreadContext.detach();
ThreadContext.setApplication(targetApplication);
ThreadContext.setRequestCycle(null);
return handle;
} catch (RuntimeException e) {
try {
handle.close();
} catch (RuntimeException e2) {
e.addSuppressed(e2);
}
throw e;
}
}
protected WebApplication getTargetApplication() {
return MoreObjects.firstNonNull(application, defaultApplication);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ApplicationExecutionContext) {
if (obj == this) {
return true;
}
ApplicationExecutionContext other = (ApplicationExecutionContext) this;
return new EqualsBuilder()
.append(application, other.application)
.append(getTargetApplication(), other.getTargetApplication())
.build();
}
return false;
}
@Override
public int hashCode() {
return new HashCodeBuilder()
.append(application)
.append(getTargetApplication())
.build();
}
}
private final class RequestCycleExecutionContext extends AbstractExecutionContext {
@Override
public ITearDownHandle open() {
if (RequestCycle.get() != null) {
return ExecutionContexts.noOp().open();
}
WebApplication application = WebApplication.get();
final ServletContext context = application.getServletContext();
final HttpSession newHttpSession = new MockHttpSession(context);
final MockHttpServletRequest servletRequest = new ContextConfiguredMockHttpServletRequest(application,
newHttpSession, context);
final MockHttpServletResponse servletResponse = new MockHttpServletResponse(servletRequest);
servletRequest.initialize();
servletResponse.initialize();
final ServletWebRequest webRequest = new ServletWebRequest(servletRequest, servletRequest.getFilterPrefix());
final WebResponse webResponse = new BufferedWebResponse(new ServletWebResponse(webRequest, servletResponse));
RequestCycle requestCycle = application.createRequestCycle(webRequest, webResponse);
ITearDownHandle handle = new ITearDownHandle() {
@Override
public void close() {
ThreadContext.setSession(null);
ThreadContext.setRequestCycle(null);
}
};
try {
ThreadContext.setRequestCycle(requestCycle);
// The session will be set automatically if required
return handle;
} catch (RuntimeException e) {
try {
handle.close();
} catch (RuntimeException e2) {
e.addSuppressed(e2);
}
throw e;
}
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
@Override
public int hashCode() {
return super.hashCode();
}
}
private final class SessionExecutionContext extends AbstractExecutionContext {
private final Locale locale;
public SessionExecutionContext(Locale locale) {
this.locale = locale;
}
@Override
public ITearDownHandle open() {
Session session = Session.get();
final Locale oldLocale = session.getLocale();
if (locale == null || locale.equals(oldLocale)) {
return ExecutionContexts.noOp().open();
}
ITearDownHandle handle = new ITearDownHandle() {
@Override
public void close() {
Session.get().setLocale(oldLocale);
}
};
try {
session.setLocale(locale);
return handle;
} catch (RuntimeException e) {
try {
handle.close();
} catch (RuntimeException e2) {
e.addSuppressed(e2);
}
throw e;
}
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SessionExecutionContext) {
if (obj == this) {
return true;
}
SessionExecutionContext other = (SessionExecutionContext) this;
return new EqualsBuilder()
.append(locale, other.locale)
.build();
}
return false;
}
@Override
public int hashCode() {
return new HashCodeBuilder()
.append(locale)
.build();
}
}
private class ContextConfiguredMockHttpServletRequest extends MockHttpServletRequest {
private final Application application;
public ContextConfiguredMockHttpServletRequest(Application application, HttpSession session,
ServletContext context) {
super(application, session, context);
this.application = application;
}
@Override
public String getScheme() {
return propertyService.get(WicketMorePropertyIds.wicketBackgroundThreadContextBuilderUrlScheme(application));
}
@Override
public String getServerName() {
return propertyService.get(WicketMorePropertyIds.wicketBackgroundThreadContextBuilderUrlServerName(application));
}
@Override
public int getServerPort() {
return propertyService.get(WicketMorePropertyIds.wicketBackgroundThreadContextBuilderUrlServerPort(application));
}
@Override
public String getServletPath() {
return propertyService.get(WicketMorePropertyIds.wicketBackgroundThreadContextBuilderUrlServletPath(application));
}
@Override
public String getContextPath() {
return getServletContext().getContextPath();
}
}
}