/*
* 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.bridge.portlet;
import juzu.Consumes;
import juzu.PropertyType;
import juzu.impl.asset.AssetServer;
import juzu.impl.bridge.Bridge;
import juzu.impl.bridge.BridgeConfig;
import juzu.impl.bridge.BridgeContext;
import juzu.impl.bridge.module.ApplicationBridge;
import juzu.impl.bridge.module.ModuleContextImpl;
import juzu.impl.bridge.provided.ProvidedBridge;
import juzu.impl.bridge.spi.portlet.PortletEventBridge;
import juzu.impl.bridge.spi.portlet.PortletViewBridge;
import juzu.impl.bridge.spi.servlet.AbstractBridgeContext;
import juzu.impl.common.JUL;
import juzu.impl.fs.spi.ReadFileSystem;
import juzu.impl.fs.spi.war.WarFileSystem;
import juzu.impl.bridge.spi.portlet.PortletActionBridge;
import juzu.impl.bridge.spi.portlet.PortletResourceBridge;
import juzu.impl.common.Logger;
import juzu.impl.common.SimpleMap;
import juzu.impl.common.Tools;
import juzu.impl.inject.spi.Injector;
import juzu.impl.inject.spi.InjectorProvider;
import juzu.impl.inject.spi.spring.SpringInjector;
import juzu.impl.plugin.controller.ControllerService;
import juzu.impl.plugin.controller.ControllerResolver;
import juzu.impl.request.ControllerHandler;
import juzu.impl.resource.ResourceResolver;
import juzu.request.Phase;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.EventPortlet;
import javax.portlet.EventRequest;
import javax.portlet.EventResponse;
import javax.portlet.Portlet;
import javax.portlet.PortletConfig;
import javax.portlet.PortletContext;
import javax.portlet.PortletException;
import javax.portlet.PortletMode;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.ResourceRequest;
import javax.portlet.ResourceResponse;
import javax.portlet.ResourceServingPortlet;
import javax.portlet.UnavailableException;
import javax.portlet.WindowState;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
/** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
public class JuzuPortlet implements Portlet, ResourceServingPortlet, EventPortlet {
/** . */
public static final PropertyType<PortletMode> PORTLET_MODE = new PropertyType<PortletMode>(){};
/** . */
public static final PropertyType<WindowState> WINDOW_STATE = new PropertyType<WindowState>(){};
/** . */
private Bridge bridge;
/** . */
private PortletConfig config;
public void init(final PortletConfig config) throws PortletException {
//
final PortletContext context = config.getPortletContext();
//
AssetServer server = (AssetServer)context.getAttribute("asset.server");
if (server == null) {
server = new AssetServer();
context.setAttribute("asset.server", server);
}
// The portlet logger
Logger portletLogger = JUL.getLogger(JuzuPortlet.class.getName() + "." + config.getPortletName());
//
BridgeConfig bridgeConfig;
try {
bridgeConfig = new BridgeConfig(portletLogger, new SimpleMap<String, String>() {
@Override
protected Iterator<String> keys() {
return BridgeConfig.NAMES.iterator();
}
@Override
public String get(Object key) {
if (BridgeConfig.APP_NAME.equals(key)) {
return getApplicationName(config);
} else if (BridgeConfig.INJECT.equals(key)) {
// Cascade:
// 1/ portlet init param
// 2/ servket context init param
String inject = config.getInitParameter((String)key);
if (inject == null) {
inject = context.getInitParameter((String)key);
}
return inject;
} else if (BridgeConfig.NAMES.contains(key)) {
return config.getInitParameter((String)key);
} else {
return null;
}
}
});
}
catch (Exception e) {
String msg = "Could not find an application to start";
if (e instanceof PortletException) {
String nested = e.getMessage();
if (nested != null) {
msg += ":" + nested;
}
throw Tools.initCause(new UnavailableException(msg), e.getCause());
} else {
throw new PortletException(msg, e);
}
}
//
final BridgeContext bridgeContext = new AbstractBridgeContext() {
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
final ResourceResolver resolver = new ResourceResolver() {
public URL resolve(String uri) {
try {
return context.getResource(uri);
}
catch (MalformedURLException e) {
return null;
}
}
};
public ReadFileSystem<?> getResourcePath() {
return WarFileSystem.create(context, "/WEB-INF/");
}
public ReadFileSystem<?> getClassPath() {
return WarFileSystem.create(context, "/WEB-INF/classes/");
}
public ClassLoader getClassLoader() {
return classLoader;
}
public String getInitParameter(String name) {
return context.getInitParameter(name);
}
public ResourceResolver getResolver() {
return resolver;
}
public Object getAttribute(String key) {
return context.getAttribute(key);
}
public void setAttribute(String key, Object value) {
context.setAttribute(key, value);
}
};
//
InjectorProvider injectorProvider = bridgeConfig.injectorProvider;
if (injectorProvider == null) {
throw new UnavailableException("No inject implementation selected");
} else {
portletLogger.info("Using inject implementation " + injectorProvider.getValue());
}
//
Injector injector = injectorProvider.get();
if (injector instanceof SpringInjector) {
SpringInjector springInjector = (SpringInjector)injector;
Object parent = context.getAttribute("org.springframework.web.context.WebApplicationContext.ROOT");
if (parent != null) {
springInjector.setParent(parent);
}
}
//
ResourceResolver resolver = new ResourceResolver() {
public URL resolve(String uri) {
try {
return context.getResource(uri);
}
catch (MalformedURLException e) {
return null;
}
}
};
//
ModuleContextImpl module = (ModuleContextImpl)context.getAttribute("juzu.module");
if (module == null) {
context.setAttribute("juzu.module", module = new ModuleContextImpl(portletLogger, bridgeContext, resolver));
}
//
Bridge bridge;
if (injector.isProvided()) {
bridge = new ProvidedBridge(bridgeContext, bridgeConfig, server, resolver, injector);
} else {
bridge = new ApplicationBridge(module, bridgeContext, bridgeConfig, server, resolver, injector);
}
//
this.config = config;
this.bridge = bridge;
}
/**
* Returns the application name to use using the <code>juzu.app_name</code> init parameter of the portlet deployment
* descriptor. Subclass can override it to provide a custom application name.
*
* @param config the portlet config
* @return the application name
*/
protected String getApplicationName(PortletConfig config) {
return config.getInitParameter(BridgeConfig.APP_NAME);
}
private void rethrow(Throwable e) throws PortletException, IOException {
if (e instanceof PortletException) {
throw (PortletException)e;
} else if (e instanceof IOException) {
throw (IOException)e;
} else {
throw new PortletException(e);
}
}
public void processAction(ActionRequest req, ActionResponse resp) throws PortletException, IOException {
try {
PortletActionBridge requestBridge = new PortletActionBridge(bridge, req, resp, config);
requestBridge.invoke();
requestBridge.send();
}
catch (Throwable e) {
rethrow(e);
}
}
public void processEvent(EventRequest request, EventResponse response) throws PortletException, IOException {
ControllerResolver<ControllerHandler> resolver = bridge.getApplication().resolveBean(ControllerService.class).getDescriptor().getResolver();
List<ControllerHandler> handlers = resolver.resolveMethods(Phase.EVENT, null, request.getParameterMap().keySet());
//
ControllerHandler target = null;
for (ControllerHandler handler : handlers) {
Consumes consumes = handler.getMethod().getAnnotation(Consumes.class);
if (consumes.value().equals("")) {
target = handler;
// we don't break here on purpose because having empty match is less important
// than an explicit match
} else if (consumes.value().equals(request.getEvent().getName())) {
target = handler;
break;
}
}
//
if (target != null) {
try {
PortletEventBridge requestBridge = new PortletEventBridge(
bridge,
request,
response,
config,
target,
request.getParameterMap());
requestBridge.invoke();
requestBridge.send();
}
catch (Throwable e) {
rethrow(e);
}
} else {
// We just don't dispatch however we keep the same render parameters
response.setRenderParameters(request);
}
}
private boolean initialized = false;
public void render(final RenderRequest req, final RenderResponse resp) throws PortletException, IOException {
// THIS CODE SHOULD BE REMOVED
// BECAUSE IT DOES NOT BEHAVE CORRECTLY WHEN USED IN PRETTY FAIL MODE
// AND IT IS ALSO HANDLED IN THE REQUEST BRIDGE
if (!initialized) {
try {
bridge.refresh();
initialized = true;
}
catch (Exception e) {
String msg = "Could not initialize application";
if (e instanceof PortletException) {
String nested = e.getMessage();
if (nested != null) {
msg += ":" + nested;
}
throw new PortletException(msg, e.getCause());
} else {
throw new PortletException(msg, e);
}
}
}
//
try {
PortletViewBridge requestBridge = new PortletViewBridge(bridge, req, resp, config);
requestBridge.invoke();
requestBridge.send();
}
catch (Throwable e) {
rethrow(e);
}
}
public void serveResource(final ResourceRequest req, final ResourceResponse resp) throws PortletException, IOException {
// THIS CODE SHOULD BE REMOVED
// BECAUSE IT DOES NOT BEHAVE CORRECTLY WHEN USED IN PRETTY FAIL MODE
// AND IT IS ALSO HANDLED IN THE REQUEST BRIDGE
if (!initialized) {
try {
bridge.refresh();
initialized = true;
}
catch (Exception e) {
String msg = "Could not initialize application";
if (e instanceof PortletException) {
String nested = e.getMessage();
if (nested != null) {
msg += ":" + nested;
}
throw new PortletException(msg, e.getCause());
} else {
throw new PortletException(msg, e);
}
}
}
//
try {
PortletResourceBridge requestBridge = new PortletResourceBridge(bridge, req, resp, config);
requestBridge.invoke();
requestBridge.send();
}
catch (Throwable throwable) {
rethrow(throwable);
}
}
public void destroy() {
Tools.safeClose(bridge);
}
}