/*
* Copyright 2008-2010 the T2 Project ant the Others.
*
* 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 org.t2framework.confeito.plugin;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.t2framework.confeito.action.ActionContext;
import org.t2framework.confeito.adapter.ContainerAdapter;
import org.t2framework.confeito.contexts.WebApplication;
import org.t2framework.confeito.contexts.WebContext;
import org.t2framework.confeito.plugin.AbstractPlugin.PluginDefaultNavigation;
import org.t2framework.confeito.spi.Navigation;
import org.t2framework.confeito.util.Assertion;
/**
* <#if locale="en">
* <p>
* PluginProceesor is a processor interface of executing {@link Plugin} at each
* phase.This interface is the core of plugin architecture for T2 framework and
* it is not expected user to extend and use in most case.
*
* </p>
* <#else>
* <p>
*
* </p>
* </#if>
*
* @author shot
*/
public class PluginProcessor {
/**
* The {@link ContainerAdapter} for finding plugin component.
*/
protected final ContainerAdapter<?> containerAdapter;
/**
* The lock object.
*/
protected final Object lock = new Object();
/**
* Default navigation for plugin.
*/
protected static Navigation DEFAULT_NAVIGATION = PluginDefaultNavigation.INSTANCE;
/**
* <#if locale="en">
* <p>
* Construct this instance with {@link ContainerAdapter}.The given container
* adapter must not be null.
* </p>
* <#else>
* <p>
*
* </p>
* </#if>
*
* @param containerAdapter
*/
public PluginProcessor(ContainerAdapter<?> containerAdapter) {
this.containerAdapter = Assertion.notNull(containerAdapter);
}
/**
*
* <#if locale="en">
* <p>
* Invoke initialization. Executed at
* {@link Filter#init(javax.servlet.FilterConfig)}.
* </p>
* <#else>
* <p>
*
* </p>
* </#if>
*
* @param servletContext
* @param webApplication
*/
public void invokeInit(final ServletContext servletContext,
final WebApplication webApplication) {
invoke0(servletContext, new Invoker<ServletContext>() {
@Override
public ServletContext invoke(Plugin plugin,
ServletContext servletContext) {
plugin.initialize(servletContext, webApplication);
return servletContext;
}
});
}
/**
*
* <#if locale="en">
* <p>
* Notify to begin request processing.
* </p>
* <#else>
* <p>
*
* </p>
* </#if>
*
* @param request
* @param response
*/
public void beginRequest(final HttpServletRequest request,
final HttpServletResponse response) {
invoke0(request, new Invoker<HttpServletRequest>() {
@Override
public HttpServletRequest invoke(Plugin plugin,
HttpServletRequest request) {
plugin.beginRequestProcessing(request, response);
return request;
}
});
}
/**
*
* <#if locale="en">
* <p>
* Hook of end request processing.You can flush or release any resources for
* this request.
* </p>
* <#else>
* <p>
*
* </p>
* </#if>
*
* @param request
* @param response
*/
public void endRequest(final HttpServletRequest request,
final HttpServletResponse response) {
invoke0(request, new Invoker<HttpServletRequest>() {
@Override
public HttpServletRequest invoke(Plugin plugin,
HttpServletRequest request) {
plugin.endRequestProcessing(request, response);
return request;
}
});
}
/**
*
* <#if locale="en">
* <p>
* Creates {@link HttpServletRequest} or just returns original.Usually it
* does nothing but in case of some reasons to use your own request
* instance, do wrap original request using
* {@link HttpServletRequestWrapper}.
* </p>
* <#else>
* <p>
*
* </p>
* </#if>
*
* @param request
* @param response
* @return created {@link HttpServletRequest}
*/
public HttpServletRequest createRequest(final HttpServletRequest request,
final HttpServletResponse response) {
HttpServletRequest req = invoke0(request,
new Invoker<HttpServletRequest>() {
@Override
public HttpServletRequest invoke(Plugin plugin,
HttpServletRequest req) {
return plugin.createRequest(req, response);
}
});
return req;
}
/**
*
* <#if locale="en">
* <p>
* Creates {@link HttpServletRequest} or just returns original.Usually it
* does nothing but in case of some reasons to use your own request
* instance, do wrap original request using
* {@link HttpServletRequestWrapper}.
* </p>
* <#else>
* <p>
*
* </p>
* </#if>
*
* @param request
* @param response
* @return created {@link HttpServletResponse}
*/
public HttpServletResponse createResponse(final HttpServletRequest request,
final HttpServletResponse response) {
HttpServletResponse res = invoke0(response,
new Invoker<HttpServletResponse>() {
@Override
public HttpServletResponse invoke(Plugin plugin,
HttpServletResponse response) {
return plugin.createResponse(request, response);
}
});
return res;
}
/**
*
* <#if locale="en">
* <p>
* Hook just after page instance creation.You can modify original page
* instance or do AOP things or whatever do with newly created page
* instance.
* </p>
* <#else>
* <p>
*
* </p>
* </#if>
*
* @param context
* @param page
* @return navigation instance
*/
public Navigation invokeComponentCreated(final WebContext context,
final Object page) {
return invokeNavigation(page, new NavigationInvoker<Object>() {
@Override
public Navigation invoke(Plugin plugin, Object param) {
return plugin.componentCreated(context, param);
}
});
}
/**
*
* <#if locale="en">
* <p>
* Hook just before invoking an action method.
* </p>
* <#else>
* <p>
*
* </p>
* </#if>
*
* @param actionContext
* @param methodDesc
* @param page
* @param args
* @return navigation instance
*/
public Navigation beforeActionInvoke(final ActionContext actionContext,
final Method targetMethod, final Object page, final Object[] args) {
return invokeNavigation(page, new NavigationInvoker<Object>() {
@Override
public Navigation invoke(Plugin plugin, Object param) {
return plugin.beforeActionInvoke(actionContext, targetMethod,
param, args);
}
});
}
/**
*
* <#if locale="en">
* <p>
* Hook just after invoking an action method.This method must execute
* whether action invoking causes an error or not.
* </p>
* <#else>
* <p>
*
* </p>
* </#if>
*
* @param actionContext
* @param methodDesc
* @param page
* @param args
* @param result
* @return navigation instance
*/
public Navigation afterActionInvoke(final ActionContext actionContext,
final Method targetMethod, final Object page, final Object[] args,
final Navigation result) {
return invokeNavigation(page, new NavigationInvoker<Object>() {
@Override
public Navigation invoke(Plugin plugin, Object param) {
return plugin.afterActionInvoke(actionContext, targetMethod,
param, args, result);
}
});
}
/**
*
* <#if locale="en">
* <p>
* Hook just before {@link Navigation#execute(WebContext)}.
* </p>
* <#else>
* <p>
*
* </p>
* </#if>
*
* @param context
*/
public void beforeNavigation(final WebContext context) {
invoke0(context, new Invoker<WebContext>() {
@Override
public WebContext invoke(Plugin plugin, WebContext ctx) {
plugin.beforeNavigation(ctx);
return ctx;
}
});
}
/**
*
* <#if locale="en">
* <p>
* Hook just after {@link Navigation#execute(WebContext)}.This method must
* execute whether invoking {@link Navigation#execute(WebContext)} causes an
* error or not.
* </p>
* <#else>
* <p>
*
* </p>
* </#if>
*
* @param context
*/
public void afterNavigation(WebContext context) {
invoke0(context, new Invoker<WebContext>() {
@Override
public WebContext invoke(Plugin plugin, WebContext ctx) {
plugin.afterNavigation(ctx);
return ctx;
}
});
}
/**
*
* <#if locale="en">
* <p>
* Destroy any resources.Executed at {@link Filter#destroy()}.
* </p>
* <#else>
* <p>
*
* </p>
* </#if>
*
* @param servletContext
* @param webApplication
*/
public void destroy(final ServletContext servletContext,
final WebApplication webApplication) {
invoke0(servletContext, new Invoker<ServletContext>() {
@Override
public ServletContext invoke(Plugin plugin,
ServletContext servletContext) {
plugin.destroy(servletContext, webApplication);
return servletContext;
}
});
}
/**
*
* <#if locale="en">
* <p>
* Invoke {@link Plugin} by {@link Invoker}.
* </p>
* <#else>
* <p>
* </p>
* </#if>
*
* @param <T>
* @param org
* @param invoker
* @return
*/
protected <T> T invoke0(T org, Invoker<T> invoker) {
List<Plugin> plugins = getPlugins();
if (plugins == null || plugins.isEmpty()) {
return org;
}
T instance = org;
synchronized (instance) {
for (Plugin p : plugins) {
p.setContainerAdapter(containerAdapter);
instance = invoker.invoke(p, instance);
}
}
return instance;
}
/**
*
* <#if locale="en">
* <p>
* Invoke {@link NavigationInvoker} for {@link Navigation} .
* </p>
* <#else>
* <p>
* </p>
* </#if>
*
* @param <T>
* @param page
* @param invoker
* @return
*/
protected <T> Navigation invokeNavigation(T page,
NavigationInvoker<T> invoker) {
List<Plugin> plugins = getPlugins();
if (plugins == null || plugins.isEmpty()) {
return DEFAULT_NAVIGATION;
}
T param = page;
Navigation navigation = DEFAULT_NAVIGATION;
synchronized (lock) {
for (Plugin p : plugins) {
p.setContainerAdapter(containerAdapter);
navigation = invoker.invoke(p, param);
if (navigation != null
&& navigation.equals(DEFAULT_NAVIGATION) == false) {
break;
}
}
}
return navigation;
}
/**
* <#if locale="en">
* <p>
* Private invoker interface.
* </p>
* <#else>
* <p>
*
* </p>
* </#if>
*
* @author shot
*
* @param <T>
*/
private static interface Invoker<T> {
T invoke(Plugin plugin, T param);
}
/**
* <#if locale="en">
* <p>
* Private navigation result invoker interface.
* </p>
* <#else>
* <p>
*
* </p>
* </#if>
*
* @author shot
*
* @param <T>
*/
private static interface NavigationInvoker<T> {
Navigation invoke(Plugin plugin, T param);
}
/**
*
* <#if locale="en">
* <p>
* Get list of {@link Plugin} which is handled by this processor.
* </p>
* <#else>
* <p>
*
* </p>
* </#if>
*
* @return list of plugin
*/
public List<Plugin> getPlugins() {
if (this.containerAdapter.hasComponent(Plugin.class)) {
return containerAdapter.getComponents(Plugin.class);
} else {
return Collections.emptyList();
}
}
}