/*
* Copyright 2013 eXo Platform SAS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package juzu.impl.bridge.spi.web;
import juzu.PropertyMap;
import juzu.PropertyType;
import juzu.Response;
import juzu.Scope;
import juzu.asset.AssetLocation;
import juzu.impl.bridge.Bridge;
import juzu.impl.common.Logger;
import juzu.impl.common.RunMode;
import juzu.impl.common.UriBuilder;
import juzu.impl.inject.spi.InjectorProvider;
import juzu.impl.request.ContextualParameter;
import juzu.impl.request.ControllerHandler;
import juzu.request.ClientContext;
import juzu.request.RequestParameter;
import juzu.request.ResponseParameter;
import juzu.impl.bridge.spi.DispatchBridge;
import juzu.impl.common.MimeType;
import juzu.impl.common.MethodHandle;
import juzu.impl.plugin.controller.ControllerService;
import juzu.impl.bridge.spi.ScopedContext;
import juzu.impl.request.Request;
import juzu.impl.bridge.spi.RequestBridge;
import juzu.impl.common.Tools;
import juzu.impl.router.PathParam;
import juzu.impl.router.Route;
import juzu.impl.router.RouteMatch;
import juzu.request.ApplicationContext;
import juzu.request.HttpContext;
import juzu.request.Phase;
import juzu.request.SecurityContext;
import juzu.request.UserContext;
import juzu.request.WindowContext;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.RejectedExecutionException;
/** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
public abstract class WebRequestBridge implements RequestBridge, WindowContext {
/** . */
final Bridge bridge;
/** . */
final juzu.impl.bridge.spi.web.Handler handler;
/** . */
final WebBridge http;
/** . */
final Phase phase;
/** . */
final ControllerHandler<?> target;
/** . */
protected Request request;
/** . */
protected Map<String, RequestParameter> requestParameters;
/** . */
protected Response response;
WebRequestBridge(
Bridge bridge,
juzu.impl.bridge.spi.web.Handler handler,
WebBridge http,
Phase phase,
ControllerHandler<?> target,
Map<String, RequestParameter> requestParameters) {
this.requestParameters = requestParameters;
this.bridge = bridge;
this.target = target;
this.handler = handler;
this.http = http;
this.request = null;
this.phase = phase;
}
//
@Override
public Charset getDefaultRequestEncoding() {
return bridge.getConfig().requestEncoding;
}
@Override
public Map<ContextualParameter, Object> getContextualArguments(Set<ContextualParameter> parameters) {
return Collections.emptyMap();
}
public RunMode getRunMode() {
return bridge.getRunMode();
}
public Phase getPhase() {
return phase;
}
public Logger getLogger(String name) {
return http.getLogger(name);
}
public Map<String, RequestParameter> getRequestArguments() {
return requestParameters;
}
public MethodHandle getTarget() {
return target.getHandle();
}
public <T> T getProperty(PropertyType<T> propertyType) {
if (RunMode.PROPERTY.equals(propertyType)) {
return propertyType.cast(bridge.getRunMode());
} else if (InjectorProvider.PROPERTY.equals(propertyType)) {
return propertyType.cast(bridge.getConfig().injectorProvider);
} else if (PropertyType.PATH.equals(propertyType)) {
return propertyType.cast(http.getRequestContext().getRequestURI());
}
return null;
}
//
public final String getNamespace() {
return "window_ns";
}
public final String getId() {
return "window_id";
}
//
public ClientContext getClientContext() {
return phase == Phase.ACTION || phase == Phase.RESOURCE ? http.getClientContext() : null;
}
public final HttpContext getHttpContext() {
return http.getHttpContext();
}
public final WindowContext getWindowContext() {
return this;
}
public final SecurityContext getSecurityContext() {
return http.getSecurityContext();
}
public UserContext getUserContext() {
return http.getUserContext();
}
public ApplicationContext getApplicationContext() {
return http.getApplicationContext();
}
public final ScopedContext getScopedContext(Scope scope, boolean create) {
ScopedContext context;
switch (scope) {
case REQUEST:
context = http.getRequestScope(create);
break;
case FLASH:
context = http.getFlashScope(create);
break;
case SESSION:
context = http.getSessionScope(create);
break;
default:
throw new UnsupportedOperationException("Unsupported scope " + scope);
}
return context;
}
public final DispatchBridge createDispatch(Phase phase, final MethodHandle target, final Map<String, ResponseParameter> parameters) {
ControllerHandler handler = bridge.getApplication().resolveBean(ControllerService.class).getDescriptor().getMethodByHandle(target);
//
Route route = this.handler.getRoute(handler.getHandle());
if (route == null) {
if (bridge.getApplication().resolveBean(ControllerService.class).getResolver().isIndex(handler)) {
route = this.handler.getRoot();
}
}
//
if (route != null) {
Map<String, String> params;
if (parameters.isEmpty()) {
params = Collections.emptyMap();
} else {
params = new HashMap<String, String>(parameters.size());
for (ResponseParameter parameter : parameters.values()) {
params.put(parameter.getName(), parameter.get(0));
}
}
//
final RouteMatch match = route.matches(params);
if (match != null) {
return new DispatchBridge() {
public MethodHandle getTarget() {
return target;
}
public Map<String, ResponseParameter> getParameters() {
return parameters;
}
public <T> String checkPropertyValidity(PropertyType<T> propertyType, T propertyValue) {
// For now we don't validate anything
return null;
}
public void renderURL(PropertyMap properties, MimeType mimeType, Appendable appendable) throws IOException {
// Render base URL
http.renderRequestURL(appendable);
// Render path
UriBuilder writer = new UriBuilder(appendable, mimeType);
match.render(writer);
// Retain matched parameters for filtering later
Set<String> matched = match.getMatched().isEmpty() ? Collections.<String>emptySet() : new HashSet<String>(match.getMatched().size());
for (PathParam param : match.getMatched().keySet()) {
matched.add(param.getName());
}
// Render remaining parameters which have not been rendered yet
for (ResponseParameter parameter : parameters.values()) {
if (!matched.contains(parameter.getName())) {
for (int i = 0;i < parameter.size();i++) {
writer.appendQueryParameter(parameter.getEncoding(), parameter.getName(), parameter.get(i));
}
}
}
}
};
} else {
throw new IllegalArgumentException("The parameters " + parameters + " are not valid");
}
} else {
throw new UnsupportedOperationException("handle me gracefully method not mapped " + handler.getHandle());
}
}
public void setResponse(Response response) throws IllegalArgumentException, IOException {
this.response = response;
}
public final void begin(Request request) {
this.request = request;
}
public void end() {
this.request = null;
}
public void execute(Runnable runnable) throws RejectedExecutionException {
http.execute(runnable);
}
public void close() {
}
void invoke() throws Exception {
try {
bridge.getApplication().resolveBean(ControllerService.class).invoke(this);
} finally {
Tools.safeClose(this);
}
}
/**
* Send the response to the client.
*/
boolean send() throws Exception {
if (response instanceof Response.Error) {
Response.Error error = (Response.Error)response;
http.getRequestContext().send(error, bridge.getRunMode().getPrettyFail());
return true;
} else if (response instanceof Response.View) {
Response.View view = (Response.View)response;
Phase.View.Dispatch update = (Phase.View.Dispatch)view;
Boolean redirect = view.getProperties().getValue(PropertyType.REDIRECT_AFTER_ACTION);
if (redirect != null && !redirect) {
ControllerHandler<?> desc = this.bridge.getApplication().resolveBean(ControllerService.class).getDescriptor().getMethodByHandle(update.getTarget());
Map<String, RequestParameter> rp = Collections.emptyMap();
for (ResponseParameter parameter : update.getParameters().values()) {
if (rp.isEmpty()) {
rp = new HashMap<String, RequestParameter>();
}
RequestParameter requestParameter = RequestParameter.create(parameter.getName(), parameter.toArray());
rp.put(requestParameter.getName(), requestParameter);
}
WebViewBridge requestBridge = new WebViewBridge(bridge, handler, http, desc, rp);
requestBridge.invoke();
return requestBridge.send();
} else {
String url = update.with(MimeType.PLAIN).with(update.getProperties()).toString();
Iterable<Map.Entry<String, String[]>> headers = view.getProperties().getValues(PropertyType.HEADER);
if (headers == null) {
headers = Tools.emptyIterable();
}
http.getRequestContext().setHeaders(headers);
http.getRequestContext().sendRedirect(url);
return true;
}
}
else if (response instanceof Response.Redirect) {
Response.Redirect redirect = (Response.Redirect)response;
String url = redirect.getLocation();
http.getRequestContext().sendRedirect(url);
return true;
} else {
return false;
}
}
public void renderAssetURL(AssetLocation location, String uri, Appendable appendable) throws NullPointerException, UnsupportedOperationException, IOException {
http.getRequestContext().renderAssetURL(location, uri, appendable);
}
}