mirror of
https://github.com/apache/struts.git
synced 2026-08-31 19:35:40 +00:00
Moving the JSF code into its own plugin, minor improvement to struts 1 config
WW-1457 git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@450338 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>2.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-jsf-plugin</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Struts 2 JSF Plugin</name>
|
||||
|
||||
<distributionManagement>
|
||||
<site>
|
||||
<id>apache-site</id>
|
||||
<url>scp://people.apache.org/www/struts.apache.org/2.x/plugins/jsf</url>
|
||||
</site>
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts-core</artifactId>
|
||||
<version>1.3.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>myfaces</groupId>
|
||||
<artifactId>myfaces-jsf-api</artifactId>
|
||||
<version>1.0.9</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>mockobjects</groupId>
|
||||
<artifactId>mockobjects-core</artifactId>
|
||||
<version>0.09</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright 2006 The Apache Software Foundation.
|
||||
*
|
||||
* 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.apache.struts2.jsf;
|
||||
|
||||
import javax.faces.FacesException;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.event.PhaseId;
|
||||
|
||||
/**
|
||||
* Applies the request values to the component tree
|
||||
*/
|
||||
public class ApplyRequestValuesInterceptor extends FacesInterceptor {
|
||||
|
||||
private static final long serialVersionUID = -1471180154211835323L;
|
||||
|
||||
/**
|
||||
* Apply Request Values (JSF.2.2.2)
|
||||
*
|
||||
* @param viewId
|
||||
* The view id
|
||||
* @param facesContext
|
||||
* The faces context
|
||||
* @return true, if response is complete
|
||||
*/
|
||||
protected boolean executePhase(String viewId, FacesContext facesContext)
|
||||
throws FacesException {
|
||||
boolean skipFurtherProcessing = false;
|
||||
if (log.isTraceEnabled())
|
||||
log.trace("entering applyRequestValues");
|
||||
|
||||
informPhaseListenersBefore(facesContext, PhaseId.APPLY_REQUEST_VALUES);
|
||||
|
||||
try {
|
||||
if (isResponseComplete(facesContext, "applyRequestValues", true)) {
|
||||
// have to return right away
|
||||
return true;
|
||||
}
|
||||
if (shouldRenderResponse(facesContext, "applyRequestValues", true)) {
|
||||
skipFurtherProcessing = true;
|
||||
}
|
||||
|
||||
facesContext.getViewRoot().processDecodes(facesContext);
|
||||
} finally {
|
||||
informPhaseListenersAfter(facesContext,
|
||||
PhaseId.APPLY_REQUEST_VALUES);
|
||||
}
|
||||
|
||||
if (isResponseComplete(facesContext, "applyRequestValues", false)
|
||||
|| shouldRenderResponse(facesContext, "applyRequestValues",
|
||||
false)) {
|
||||
// since this phase is completed we don't need to return right away
|
||||
// even if the response is completed
|
||||
skipFurtherProcessing = true;
|
||||
}
|
||||
|
||||
if (!skipFurtherProcessing && log.isTraceEnabled())
|
||||
log.trace("exiting applyRequestValues");
|
||||
return skipFurtherProcessing;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright 2006 The Apache Software Foundation.
|
||||
*
|
||||
* 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.apache.struts2.jsf;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.interceptor.Interceptor;
|
||||
|
||||
/**
|
||||
* Translates JSF phases into individual interceptors, and adapts their expected
|
||||
* workflow to Action 2
|
||||
*/
|
||||
public class FacesInterceptor extends FacesSupport implements Interceptor {
|
||||
|
||||
private static final long serialVersionUID = -5418255964277566516L;
|
||||
|
||||
/**
|
||||
* Not used
|
||||
*/
|
||||
public void init() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts the phase workflow to Action 2
|
||||
*
|
||||
* @param invocation
|
||||
* The action invocation
|
||||
* @return The string result code
|
||||
*/
|
||||
public String intercept(ActionInvocation invocation) throws Exception {
|
||||
|
||||
if (isFacesEnabled(invocation.getInvocationContext())) {
|
||||
FacesContext context = FacesContext.getCurrentInstance();
|
||||
|
||||
if (context.getRenderResponse()) {
|
||||
return invocation.invoke();
|
||||
} else {
|
||||
|
||||
String viewId = invocation.getProxy().getNamespace() + '/'
|
||||
+ invocation.getProxy().getActionName();
|
||||
executePhase(viewId, context);
|
||||
|
||||
if (context.getResponseComplete()) {
|
||||
// Abort the chain as the result is done
|
||||
return null;
|
||||
} else {
|
||||
if (invocation.getResultCode() != null) {
|
||||
return invocation.getResultCode();
|
||||
} else {
|
||||
return invocation.invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return invocation.invoke();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the specific phase. The phase id is constructed as a composite
|
||||
* of the namespace and action name.
|
||||
*
|
||||
* @param viewId
|
||||
* The view id
|
||||
* @param facesContext
|
||||
* The current faces context
|
||||
* @return True if the next phases should be skipped
|
||||
*/
|
||||
protected boolean executePhase(String viewId, FacesContext facesContext) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Not used
|
||||
*/
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to process this request with the JSF phases
|
||||
*
|
||||
* @param ctx The current action context
|
||||
* @return True if it is a faces-enabled request
|
||||
*/
|
||||
protected boolean isFacesEnabled(ActionContext ctx) {
|
||||
return ctx.get(FACES_ENABLED) != null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright 2006 The Apache Software Foundation.
|
||||
*
|
||||
* 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.apache.struts2.jsf;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.faces.FacesException;
|
||||
import javax.faces.application.Application;
|
||||
import javax.faces.application.ViewHandler;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.event.PhaseId;
|
||||
|
||||
/**
|
||||
* Performs the JSF render lifecycle phase.
|
||||
*
|
||||
*/
|
||||
public class FacesRender extends FacesSupport {
|
||||
|
||||
/**
|
||||
* Executes the render phase, borrowed from MyFaces
|
||||
*
|
||||
* @param facesContext
|
||||
* The faces context
|
||||
* @throws FacesException
|
||||
* If anything goes wrong
|
||||
*/
|
||||
public void render(FacesContext facesContext) throws FacesException {
|
||||
// if the response is complete we should not be invoking the phase
|
||||
// listeners
|
||||
if (isResponseComplete(facesContext, "render", true)) {
|
||||
return;
|
||||
}
|
||||
if (log.isTraceEnabled())
|
||||
log.trace("entering renderResponse");
|
||||
|
||||
informPhaseListenersBefore(facesContext, PhaseId.RENDER_RESPONSE);
|
||||
try {
|
||||
// also possible that one of the listeners completed the response
|
||||
if (isResponseComplete(facesContext, "render", true)) {
|
||||
return;
|
||||
}
|
||||
Application application = facesContext.getApplication();
|
||||
ViewHandler viewHandler = application.getViewHandler();
|
||||
try {
|
||||
viewHandler
|
||||
.renderView(facesContext, facesContext.getViewRoot());
|
||||
} catch (IOException e) {
|
||||
throw new FacesException(e.getMessage(), e);
|
||||
}
|
||||
} finally {
|
||||
informPhaseListenersAfter(facesContext, PhaseId.RENDER_RESPONSE);
|
||||
}
|
||||
if (log.isTraceEnabled()) {
|
||||
// Note: DebugUtils Logger must also be in trace level
|
||||
// DebugUtils.traceView("View after rendering");
|
||||
}
|
||||
|
||||
if (log.isTraceEnabled())
|
||||
log.trace("exiting renderResponse");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright 2006 The Apache Software Foundation.
|
||||
*
|
||||
* 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.apache.struts2.jsf;
|
||||
|
||||
import javax.faces.application.ViewHandler;
|
||||
import javax.faces.component.UIViewRoot;
|
||||
import javax.faces.context.FacesContext;
|
||||
|
||||
import org.apache.struts2.dispatcher.StrutsResultSupport;
|
||||
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.Result;
|
||||
|
||||
/**
|
||||
* Executes the JSF render phase
|
||||
*/
|
||||
public class FacesResult extends StrutsResultSupport implements Result {
|
||||
|
||||
private static final long serialVersionUID = -3548970638740937804L;
|
||||
|
||||
/**
|
||||
* Checks to see if we need to build a new JSF ViewId from the Struts Result
|
||||
* config and then renders the result by delegating to the
|
||||
* FacesRender.render().
|
||||
*
|
||||
* @see org.apache.struts2.dispatcher.StrutsResultSupport#doExecute(java.lang.String,
|
||||
* com.opensymphony.
|
||||
*/
|
||||
protected void doExecute(String finalLocation, ActionInvocation invocation)
|
||||
throws Exception {
|
||||
performNavigation(finalLocation, FacesContext.getCurrentInstance());
|
||||
new FacesRender().render(FacesContext.getCurrentInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the Struts Result uri to the faces viewId. If they are different
|
||||
* use the Struts uri to build a new faces viewId.
|
||||
*
|
||||
* @param finalLocation
|
||||
* The result uri
|
||||
* @param facesContext
|
||||
* The FacesContext
|
||||
*/
|
||||
private void performNavigation(String finalLocation,
|
||||
FacesContext facesContext) {
|
||||
String facesViewId = facesContext.getViewRoot().getViewId();
|
||||
if (finalLocation != null) {
|
||||
if (finalLocation.equals(facesViewId) == false) {
|
||||
ViewHandler viewHandler = facesContext.getApplication()
|
||||
.getViewHandler();
|
||||
UIViewRoot viewRoot = viewHandler.createView(facesContext,
|
||||
finalLocation);
|
||||
facesContext.setViewRoot(viewRoot);
|
||||
facesContext.renderResponse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,587 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright 2006 The Apache Software Foundation.
|
||||
*
|
||||
* 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.apache.struts2.jsf;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.faces.FactoryFinder;
|
||||
import javax.faces.application.Application;
|
||||
import javax.faces.application.ApplicationFactory;
|
||||
import javax.faces.application.NavigationHandler;
|
||||
import javax.faces.application.StateManager;
|
||||
import javax.faces.application.ViewHandler;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.FacesContextFactory;
|
||||
import javax.faces.el.PropertyResolver;
|
||||
import javax.faces.el.VariableResolver;
|
||||
import javax.faces.event.ActionListener;
|
||||
import javax.faces.lifecycle.Lifecycle;
|
||||
import javax.faces.lifecycle.LifecycleFactory;
|
||||
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.StrutsException;
|
||||
import org.apache.struts2.util.ClassLoaderUtils;
|
||||
|
||||
import com.opensymphony.xwork2.Action;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.config.entities.ResultConfig;
|
||||
import com.opensymphony.xwork2.interceptor.Interceptor;
|
||||
|
||||
/**
|
||||
* * Initializes the JSF context for this request.
|
||||
* <p>
|
||||
* </P>
|
||||
* The JSF Application can additionaly be configured from the Struts.xml by
|
||||
* adding <param> tags to the jsfSetup <interceptor-ref>.
|
||||
* <p>
|
||||
* </p>
|
||||
* <b>Example struts.xml configuration:</b>
|
||||
*
|
||||
* <pre>
|
||||
* <interceptor-ref name="jsfSetup">
|
||||
* <param name="actionListener"></param>
|
||||
* <param name="defaultRenderKitId"></param>
|
||||
* <param name="supportedLocale"></param>
|
||||
* <param name="defaultLocale"></param>
|
||||
* <param name="messageBundle"></param>
|
||||
* <param name="navigationHandler">org.apache.struts2.jsf.StrutsNavigationHandler</param>
|
||||
* <param name="propertyResolver"></param>
|
||||
* <param name="stateManager"></param>
|
||||
* <param name="variableResolver">
|
||||
* org.apache.myfaces.el.VariableResolverImpl
|
||||
* ,org.apache.struts2.jsf.StrutsVariableResolver
|
||||
* </param>
|
||||
* <param name="viewHandler;">org.apache.shale.tiles.TilesViewHandler</param>
|
||||
* </interceptor-ref>
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* </p>
|
||||
* <b>Note: None of the parameters are required but all are shown in the example
|
||||
* for completeness.</b>
|
||||
*/
|
||||
public class FacesSetupInterceptor extends FacesSupport implements Interceptor {
|
||||
|
||||
private static final long serialVersionUID = -621512342655103941L;
|
||||
|
||||
private String lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
|
||||
|
||||
private FacesContextFactory facesContextFactory;
|
||||
|
||||
private Lifecycle lifecycle;
|
||||
|
||||
// jsf Application configuration
|
||||
private List<String> actionListener;
|
||||
|
||||
private String defaultRenderKitId;
|
||||
|
||||
private List<String> supportedLocale;
|
||||
|
||||
private String defaultLocale;
|
||||
|
||||
private String messageBundle;
|
||||
|
||||
private List<String> navigationHandler;
|
||||
|
||||
private List<String> propertyResolver;
|
||||
|
||||
private List<String> stateManager;
|
||||
|
||||
private List<String> variableResolver;
|
||||
|
||||
private List<String> viewHandler;
|
||||
|
||||
/**
|
||||
* Sets the lifecycle id
|
||||
*
|
||||
* @param id
|
||||
* The id
|
||||
*/
|
||||
public void setLifecycleId(String id) {
|
||||
this.lifecycleId = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the lifecycle and factories
|
||||
*/
|
||||
public void init() {
|
||||
try {
|
||||
facesContextFactory = (FacesContextFactory) FactoryFinder
|
||||
.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
|
||||
} catch (Exception ex) {
|
||||
log.debug("Unable to initialize faces", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
// Javadoc says: Lifecycle instance is shared across multiple
|
||||
// simultaneous requests, it must be implemented in a thread-safe
|
||||
// manner.
|
||||
// So we can acquire it here once:
|
||||
LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder
|
||||
.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
|
||||
lifecycle = lifecycleFactory.getLifecycle(lifecycleId);
|
||||
|
||||
Application application = ((ApplicationFactory) FactoryFinder
|
||||
.getFactory(FactoryFinder.APPLICATION_FACTORY))
|
||||
.getApplication();
|
||||
|
||||
if (actionListener != null) {
|
||||
Iterator i = actionListener.iterator();
|
||||
application
|
||||
.setActionListener((ActionListener) getApplicationObject(
|
||||
ActionListener.class, i, application
|
||||
.getActionListener()));
|
||||
}
|
||||
if (defaultRenderKitId != null && defaultRenderKitId.length() > 0) {
|
||||
application.setDefaultRenderKitId(defaultRenderKitId);
|
||||
}
|
||||
if (messageBundle != null && messageBundle.length() > 0) {
|
||||
application.setMessageBundle(messageBundle);
|
||||
}
|
||||
if (supportedLocale != null) {
|
||||
List<Locale> locales = new ArrayList<Locale>();
|
||||
for (Iterator i = supportedLocale.iterator(); i.hasNext();) {
|
||||
locales.add(toLocale((String) i.next()));
|
||||
}
|
||||
application.setSupportedLocales(locales);
|
||||
}
|
||||
if (defaultLocale != null && defaultLocale.length() > 0) {
|
||||
application.setDefaultLocale(toLocale(defaultLocale));
|
||||
}
|
||||
if (navigationHandler != null) {
|
||||
Iterator i = navigationHandler.iterator();
|
||||
application
|
||||
.setNavigationHandler((NavigationHandler) getApplicationObject(
|
||||
NavigationHandler.class, i, application
|
||||
.getNavigationHandler()));
|
||||
}
|
||||
if (propertyResolver != null) {
|
||||
Iterator i = propertyResolver.iterator();
|
||||
application
|
||||
.setPropertyResolver((PropertyResolver) getApplicationObject(
|
||||
PropertyResolver.class, i, application
|
||||
.getPropertyResolver()));
|
||||
}
|
||||
if (stateManager != null) {
|
||||
Iterator i = stateManager.iterator();
|
||||
application.setStateManager((StateManager) getApplicationObject(
|
||||
StateManager.class, i, application.getStateManager()));
|
||||
}
|
||||
if (variableResolver != null) {
|
||||
Iterator i = variableResolver.iterator();
|
||||
application
|
||||
.setVariableResolver((VariableResolver) getApplicationObject(
|
||||
VariableResolver.class, i, application
|
||||
.getVariableResolver()));
|
||||
}
|
||||
if (viewHandler != null) {
|
||||
Iterator i = viewHandler.iterator();
|
||||
application.setViewHandler((ViewHandler) getApplicationObject(
|
||||
ViewHandler.class, i, application.getViewHandler()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the faces context for other phases.
|
||||
*
|
||||
* @param invocation
|
||||
* The action invocation
|
||||
*/
|
||||
public String intercept(ActionInvocation invocation) throws Exception {
|
||||
if (facesContextFactory != null) {
|
||||
if (isFacesAction(invocation)) {
|
||||
|
||||
invocation.getInvocationContext().put(
|
||||
FacesInterceptor.FACES_ENABLED, Boolean.TRUE);
|
||||
|
||||
FacesContext facesContext = facesContextFactory
|
||||
.getFacesContext(ServletActionContext
|
||||
.getServletContext(), ServletActionContext
|
||||
.getRequest(), ServletActionContext
|
||||
.getResponse(), lifecycle);
|
||||
|
||||
setLifecycle(lifecycle);
|
||||
|
||||
try {
|
||||
return invocation.invoke();
|
||||
} finally {
|
||||
facesContext.release();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new StrutsException(
|
||||
"Unable to initialize jsf interceptors probably due missing JSF implementation libraries",
|
||||
invocation.getProxy().getConfig());
|
||||
}
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the lifecycle and factories
|
||||
*/
|
||||
public void destroy() {
|
||||
facesContextFactory = null;
|
||||
lifecycle = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this action mapping will be have a JSF view
|
||||
*
|
||||
* @param inv
|
||||
* The action invocation
|
||||
* @return True if the JSF interceptors should fire
|
||||
*/
|
||||
protected boolean isFacesAction(ActionInvocation inv) {
|
||||
ActionConfig config = inv.getProxy().getConfig();
|
||||
if (config != null) {
|
||||
ResultConfig resultConfig = config.getResults().get(Action.SUCCESS);
|
||||
Class resClass = null;
|
||||
try {
|
||||
resClass = Class.forName(resultConfig.getClassName());
|
||||
} catch (ClassNotFoundException ex) {
|
||||
log.warn(
|
||||
"Can't find result class, ignoring as a faces request",
|
||||
ex);
|
||||
}
|
||||
if (resClass != null) {
|
||||
if (resClass.isAssignableFrom(FacesResult.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an object from a list of class names. This method supports
|
||||
* creating the objects using constructor delegation, if the requested class
|
||||
* supports it. Classes will be imbedded from top to bottom in the list with
|
||||
* the last class listed being the one that will be returned.
|
||||
*
|
||||
* @param interfaceClass
|
||||
* The Class type that is expected to be returned
|
||||
* @param classNamesIterator
|
||||
* An Iterator for a list of Strings that represent the class
|
||||
* names
|
||||
* @param defaultObject
|
||||
* The current Object that the jsf Application has set
|
||||
* @return
|
||||
*/
|
||||
private Object getApplicationObject(Class interfaceClass,
|
||||
Iterator classNamesIterator, Object defaultObject) {
|
||||
Object current = defaultObject;
|
||||
|
||||
while (classNamesIterator.hasNext()) {
|
||||
String implClassName = (String) classNamesIterator.next();
|
||||
Class implClass = null;
|
||||
|
||||
try {
|
||||
implClass = ClassLoaderUtils.loadClass(implClassName, this
|
||||
.getClass());
|
||||
} catch (ClassNotFoundException e1) {
|
||||
throw new IllegalArgumentException("Class " + implClassName
|
||||
+ " was not found.");
|
||||
}
|
||||
|
||||
// check, if class is of expected interface type
|
||||
if (!interfaceClass.isAssignableFrom(implClass)) {
|
||||
throw new IllegalArgumentException("Class " + implClassName
|
||||
+ " is no " + interfaceClass.getName());
|
||||
}
|
||||
|
||||
if (current == null) {
|
||||
// nothing to decorate
|
||||
try {
|
||||
current = implClass.newInstance();
|
||||
} catch (InstantiationException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw new StrutsException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw new StrutsException(e);
|
||||
}
|
||||
} else {
|
||||
// let's check if class supports the decorator pattern
|
||||
try {
|
||||
Constructor delegationConstructor = implClass
|
||||
.getConstructor(new Class[] { interfaceClass });
|
||||
// impl class supports decorator pattern,
|
||||
try {
|
||||
// create new decorator wrapping current
|
||||
current = delegationConstructor
|
||||
.newInstance(new Object[] { current });
|
||||
} catch (InstantiationException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw new StrutsException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw new StrutsException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw new StrutsException(e);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
// no decorator pattern support
|
||||
try {
|
||||
current = implClass.newInstance();
|
||||
} catch (InstantiationException e1) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw new StrutsException(e);
|
||||
} catch (IllegalAccessException e1) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw new StrutsException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a comma delimited string of class names and stores the names in an
|
||||
* <code>ArrayList</code>. The incoming <code>String</code> will be
|
||||
* cleaned of any whitespace characters before the class names are stored.
|
||||
*
|
||||
* @param actionListener
|
||||
* A comma delimited string of class names
|
||||
*/
|
||||
public void setActionListener(String actionListener) {
|
||||
if (this.actionListener == null) {
|
||||
this.actionListener = new ArrayList<String>();
|
||||
}
|
||||
String clean = actionListener.replaceAll("[ \t\r\n]", "");
|
||||
String[] actionListenerNames = clean.split(",");
|
||||
|
||||
for (int i = 0; i < actionListenerNames.length; i++) {
|
||||
this.actionListener.add(actionListenerNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A <code>String</code> to be used as the defaultRenderKitId for the jsf
|
||||
* application. The incoming <code>String</code> will be cleaned of
|
||||
* whitespace characters.
|
||||
*
|
||||
* @param defaultRenderKitId
|
||||
* The defaultRenderKitId
|
||||
*/
|
||||
public void setDefaultRenderKitId(String defaultRenderKitId) {
|
||||
String clean = defaultRenderKitId.replaceAll("[ \t\r\n]", "");
|
||||
this.defaultRenderKitId = clean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a comma delimited string of local names and stores the names in an
|
||||
* <code>ArrayList</code>. The incoming <code>String</code> will be
|
||||
* cleaned of any whitespace characters before the class names are stored.
|
||||
*
|
||||
* @param supportedLocale
|
||||
* A comma delimited string of local names
|
||||
*/
|
||||
public void setSupportedLocale(String supportedLocale) {
|
||||
if (this.supportedLocale == null) {
|
||||
this.supportedLocale = new ArrayList<String>();
|
||||
}
|
||||
String clean = supportedLocale.replaceAll("[ \t\r\n]", "");
|
||||
String[] supportedLocaleNames = clean.split(",");
|
||||
|
||||
for (int i = 0; i < supportedLocaleNames.length; i++) {
|
||||
this.supportedLocale.add(supportedLocaleNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a String representation of the defaultLocale. The incoming
|
||||
* <code>String</code> will be cleaned of any whitespace characters before
|
||||
* the class names are stored.
|
||||
*
|
||||
* @param defaultLocale
|
||||
* The default local
|
||||
*/
|
||||
public void setDefaultLocale(String defaultLocale) {
|
||||
String clean = defaultLocale.replaceAll("[ \t\r\n]", "");
|
||||
this.defaultLocale = clean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the messageBundle to be used to configure the jsf Application.
|
||||
*
|
||||
* @param messageBundle
|
||||
* The messageBundle
|
||||
*/
|
||||
public void setMessageBundle(String messageBundle) {
|
||||
String clean = messageBundle.replaceAll("[ \t\r\n]", "");
|
||||
this.messageBundle = clean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a comma delimited string of class names and stores the names in an
|
||||
* <code>ArrayList</code>. The incoming <code>String</code> will be
|
||||
* cleaned of any whitespace characters before the class names are stored.
|
||||
*
|
||||
* @param navigationHandlerName
|
||||
* A comma delimited string of class names
|
||||
*/
|
||||
public void setNavigationHandler(String navigationHandlerName) {
|
||||
if (navigationHandler == null) {
|
||||
navigationHandler = new ArrayList<String>();
|
||||
}
|
||||
String clean = navigationHandlerName.replaceAll("[ \t\r\n]", "");
|
||||
String[] navigationHandlerNames = clean.split(",");
|
||||
|
||||
for (int i = 0; i < navigationHandlerNames.length; i++) {
|
||||
navigationHandler.add(navigationHandlerNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a comma delimited string of class names and stores the names in an
|
||||
* <code>ArrayList</code>. The incoming <code>String</code> will be
|
||||
* cleaned of any whitespace characters before the class names are stored.
|
||||
*
|
||||
* @param propertyResolverName
|
||||
* A comma delimited string of class names
|
||||
*/
|
||||
public void setPropertyResolver(String propertyResolverName) {
|
||||
if (propertyResolver == null) {
|
||||
propertyResolver = new ArrayList<String>();
|
||||
}
|
||||
String clean = propertyResolverName.replaceAll("[ \t\r\n]", "");
|
||||
String[] propertyResolverNames = clean.split(",");
|
||||
|
||||
for (int i = 0; i < propertyResolverNames.length; i++) {
|
||||
propertyResolver.add(propertyResolverNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a comma delimited string of class names and stores the names in an
|
||||
* <code>ArrayList</code>. The incoming <code>String</code> will be
|
||||
* cleaned of any whitespace characters before the class names are stored.
|
||||
*
|
||||
* @param stateManagerName
|
||||
* A comma delimited string of class names
|
||||
*/
|
||||
public void setStateManager(String stateManagerName) {
|
||||
if (stateManager == null) {
|
||||
stateManager = new ArrayList<String>();
|
||||
}
|
||||
String clean = stateManagerName.replaceAll("[ \t\r\n]", "");
|
||||
String[] stateManagerNames = clean.split(",");
|
||||
|
||||
for (int i = 0; i < stateManagerNames.length; i++) {
|
||||
stateManager.add(stateManagerNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a comma delimited string of class names and stores the names in an
|
||||
* <code>ArrayList</code>. The incoming <code>String</code> will be
|
||||
* cleaned of any whitespace characters before the class names are stored.
|
||||
*
|
||||
* @param variableResolverName
|
||||
* A comma delimited string of class names
|
||||
*/
|
||||
public void setVariableResolver(String variableResolverName) {
|
||||
if (variableResolver == null) {
|
||||
variableResolver = new ArrayList<String>();
|
||||
}
|
||||
String clean = variableResolverName.replaceAll("[ \t\r\n]", "");
|
||||
String[] variableResolverNames = clean.split(",");
|
||||
|
||||
for (int i = 0; i < variableResolverNames.length; i++) {
|
||||
variableResolver.add(variableResolverNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a comma delimited string of class names and stores the names in an
|
||||
* <code>ArrayList</code>. The incoming <code>String</code> will be
|
||||
* cleaned of any whitespace characters before the class names are stored.
|
||||
*
|
||||
* @param viewHandlerName
|
||||
* A comma delimited string of class names
|
||||
*/
|
||||
public void setViewHandler(String viewHandlerName) {
|
||||
if (viewHandler == null) {
|
||||
viewHandler = new ArrayList<String>();
|
||||
}
|
||||
String[] viewHandlerNames = viewHandlerName
|
||||
.split(",^[ \t\r\n]+|[ \t\r\n]+$");
|
||||
|
||||
for (int i = 0; i < viewHandlerNames.length; i++) {
|
||||
viewHandler.add(viewHandlerNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a locale string to <code>Locale</code> class. Accepts both '_'
|
||||
* and '-' as separators for locale components.
|
||||
*
|
||||
* @param localeString
|
||||
* string representation of a locale
|
||||
* @return Locale instance, compatible with the string representation
|
||||
*/
|
||||
private Locale toLocale(String localeString) {
|
||||
if ((localeString == null) || (localeString.length() == 0)) {
|
||||
Locale locale = Locale.getDefault();
|
||||
if (log.isWarnEnabled())
|
||||
log
|
||||
.warn("Locale name in faces-config.xml null or empty, setting locale to default locale : "
|
||||
+ locale.toString());
|
||||
return locale;
|
||||
}
|
||||
|
||||
int separatorCountry = localeString.indexOf('_');
|
||||
char separator;
|
||||
if (separatorCountry >= 0) {
|
||||
separator = '_';
|
||||
} else {
|
||||
separatorCountry = localeString.indexOf('-');
|
||||
separator = '-';
|
||||
}
|
||||
|
||||
String language, country, variant;
|
||||
if (separatorCountry < 0) {
|
||||
language = localeString;
|
||||
country = variant = "";
|
||||
} else {
|
||||
language = localeString.substring(0, separatorCountry);
|
||||
|
||||
int separatorVariant = localeString.indexOf(separator,
|
||||
separatorCountry + 1);
|
||||
if (separatorVariant < 0) {
|
||||
country = localeString.substring(separatorCountry + 1);
|
||||
variant = "";
|
||||
} else {
|
||||
country = localeString.substring(separatorCountry + 1,
|
||||
separatorVariant);
|
||||
variant = localeString.substring(separatorVariant + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return new Locale(language, country, variant);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright 2006 The Apache Software Foundation.
|
||||
*
|
||||
* 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.apache.struts2.jsf;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.event.PhaseEvent;
|
||||
import javax.faces.event.PhaseId;
|
||||
import javax.faces.event.PhaseListener;
|
||||
import javax.faces.lifecycle.Lifecycle;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
|
||||
/**
|
||||
* Provides common lifecycle phase methods needed by interceptors and results.
|
||||
*/
|
||||
public class FacesSupport {
|
||||
|
||||
private static final String LIFECYCLE_KEY = "lifecycle";
|
||||
|
||||
/** Log instance for each class */
|
||||
protected Log log;
|
||||
|
||||
/**
|
||||
* Marker key for the ActionContext to dictate whether to treat the request
|
||||
* as a JSF faces request and therefore process the Faces phases
|
||||
*/
|
||||
protected static final String FACES_ENABLED = "facesEnabled";
|
||||
|
||||
/** Initializes log instance for the instance object */
|
||||
protected FacesSupport() {
|
||||
log = LogFactory.getLog(getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the shared lifecycle for this request
|
||||
*
|
||||
* @return The lifecycle
|
||||
*/
|
||||
private Lifecycle getLifecycle() {
|
||||
return (Lifecycle) ActionContext.getContext().get(LIFECYCLE_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the lifecycle for this request
|
||||
*
|
||||
* @param lifecycle
|
||||
* The lifecycle
|
||||
*/
|
||||
protected void setLifecycle(Lifecycle lifecycle) {
|
||||
ActionContext.getContext().put(LIFECYCLE_KEY, lifecycle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs phase listeners before a phase is executed
|
||||
*
|
||||
* @param facesContext
|
||||
* The current faces context
|
||||
* @param phaseId
|
||||
* The phase id about to be executed
|
||||
*/
|
||||
protected void informPhaseListenersBefore(FacesContext facesContext,
|
||||
PhaseId phaseId) {
|
||||
Lifecycle lifecycle = getLifecycle();
|
||||
PhaseListener[] phaseListeners = lifecycle.getPhaseListeners();
|
||||
for (int i = 0; i < phaseListeners.length; i++) {
|
||||
PhaseListener phaseListener = phaseListeners[i];
|
||||
int listenerPhaseId = phaseListener.getPhaseId().getOrdinal();
|
||||
if (listenerPhaseId == PhaseId.ANY_PHASE.getOrdinal()
|
||||
|| listenerPhaseId == phaseId.getOrdinal()) {
|
||||
phaseListener.beforePhase(new PhaseEvent(FacesContext
|
||||
.getCurrentInstance(), phaseId, lifecycle));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs phase listeners after a phase is executed
|
||||
*
|
||||
* @param facesContext
|
||||
* The current faces context
|
||||
* @param phaseId
|
||||
* The phase id that was executed
|
||||
*/
|
||||
protected void informPhaseListenersAfter(FacesContext facesContext,
|
||||
PhaseId phaseId) {
|
||||
Lifecycle lifecycle = getLifecycle();
|
||||
PhaseListener[] phaseListeners = lifecycle.getPhaseListeners();
|
||||
for (int i = 0; i < phaseListeners.length; i++) {
|
||||
PhaseListener phaseListener = phaseListeners[i];
|
||||
int listenerPhaseId = phaseListener.getPhaseId().getOrdinal();
|
||||
if (listenerPhaseId == PhaseId.ANY_PHASE.getOrdinal()
|
||||
|| listenerPhaseId == phaseId.getOrdinal()) {
|
||||
phaseListener.afterPhase(new PhaseEvent(FacesContext
|
||||
.getCurrentInstance(), phaseId, lifecycle));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the response has been completed. Mainly used for better
|
||||
* debugging messages.
|
||||
*
|
||||
* @param facesContext
|
||||
* The current faces context
|
||||
* @param phase
|
||||
* The phase id in execution
|
||||
* @param before
|
||||
* Whether the phase has been executed or not
|
||||
* @return True if the response is complete
|
||||
*/
|
||||
protected boolean isResponseComplete(FacesContext facesContext,
|
||||
String phase, boolean before) {
|
||||
boolean flag = false;
|
||||
if (facesContext.getResponseComplete()) {
|
||||
if (log.isDebugEnabled())
|
||||
log
|
||||
.debug("exiting from lifecycle.execute in "
|
||||
+ phase
|
||||
+ " because getResponseComplete is true from one of the "
|
||||
+ (before ? "before" : "after") + " listeners");
|
||||
flag = true;
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see the render phase should be executed next. Mainly used for
|
||||
* better debugging messages.
|
||||
*
|
||||
* @param facesContext
|
||||
* The current faces context
|
||||
* @param phase
|
||||
* The phase id in execution
|
||||
* @param before
|
||||
* Whether the phase has been executed or not
|
||||
* @return True if the response is complete
|
||||
*/
|
||||
protected boolean shouldRenderResponse(FacesContext facesContext,
|
||||
String phase, boolean before) {
|
||||
boolean flag = false;
|
||||
if (facesContext.getRenderResponse()) {
|
||||
if (log.isDebugEnabled())
|
||||
log.debug("exiting from lifecycle.execute in " + phase
|
||||
+ " because getRenderResponse is true from one of the "
|
||||
+ (before ? "before" : "after") + " listeners");
|
||||
flag = true;
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright 2006 The Apache Software Foundation.
|
||||
*
|
||||
* 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.apache.struts2.jsf;
|
||||
|
||||
import javax.faces.FacesException;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.event.PhaseId;
|
||||
|
||||
/**
|
||||
* Invokes the application
|
||||
*/
|
||||
public class InvokeApplicationInterceptor extends FacesInterceptor {
|
||||
|
||||
private static final long serialVersionUID = -7388153356410171208L;
|
||||
|
||||
/**
|
||||
* Invoke Application (JSF.2.2.5)
|
||||
*
|
||||
* @param viewId
|
||||
* The view id
|
||||
* @param facesContext
|
||||
* The faces context
|
||||
* @return true, if response is complete
|
||||
*/
|
||||
protected boolean executePhase(String viewId, FacesContext facesContext)
|
||||
throws FacesException {
|
||||
boolean skipFurtherProcessing = false;
|
||||
if (log.isTraceEnabled())
|
||||
log.trace("entering invokeApplication");
|
||||
|
||||
informPhaseListenersBefore(facesContext, PhaseId.INVOKE_APPLICATION);
|
||||
|
||||
try {
|
||||
if (isResponseComplete(facesContext, "invokeApplication", true)) {
|
||||
// have to return right away
|
||||
return true;
|
||||
}
|
||||
if (shouldRenderResponse(facesContext, "invokeApplication", true)) {
|
||||
skipFurtherProcessing = true;
|
||||
}
|
||||
|
||||
facesContext.getViewRoot().processApplication(facesContext);
|
||||
} finally {
|
||||
informPhaseListenersAfter(facesContext, PhaseId.INVOKE_APPLICATION);
|
||||
}
|
||||
|
||||
if (isResponseComplete(facesContext, "invokeApplication", false)
|
||||
|| shouldRenderResponse(facesContext, "invokeApplication",
|
||||
false)) {
|
||||
// since this phase is completed we don't need to return right away
|
||||
// even if the response is completed
|
||||
skipFurtherProcessing = true;
|
||||
}
|
||||
|
||||
if (!skipFurtherProcessing && log.isTraceEnabled())
|
||||
log.trace("exiting invokeApplication ");
|
||||
|
||||
return skipFurtherProcessing;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright 2006 The Apache Software Foundation.
|
||||
*
|
||||
* 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.apache.struts2.jsf;
|
||||
|
||||
import javax.faces.FacesException;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.event.PhaseId;
|
||||
|
||||
/**
|
||||
* Processes validations on the component tree
|
||||
*/
|
||||
public class ProcessValidationsInterceptor extends FacesInterceptor {
|
||||
|
||||
private static final long serialVersionUID = 8785236570688278147L;
|
||||
|
||||
/**
|
||||
* Process Validations (JSF.2.2.3)
|
||||
*
|
||||
* @param viewId
|
||||
* The view id
|
||||
* @param facesContext
|
||||
* The faces context
|
||||
* @return true, if response is complete
|
||||
*/
|
||||
protected boolean executePhase(String viewId, FacesContext facesContext)
|
||||
throws FacesException {
|
||||
boolean skipFurtherProcessing = false;
|
||||
if (log.isTraceEnabled())
|
||||
log.trace("entering processValidations");
|
||||
|
||||
informPhaseListenersBefore(facesContext, PhaseId.PROCESS_VALIDATIONS);
|
||||
|
||||
try {
|
||||
if (isResponseComplete(facesContext, "processValidations", true)) {
|
||||
// have to return right away
|
||||
return true;
|
||||
}
|
||||
if (shouldRenderResponse(facesContext, "processValidations", true)) {
|
||||
skipFurtherProcessing = true;
|
||||
}
|
||||
|
||||
facesContext.getViewRoot().processValidators(facesContext);
|
||||
} finally {
|
||||
informPhaseListenersAfter(facesContext, PhaseId.PROCESS_VALIDATIONS);
|
||||
}
|
||||
|
||||
if (isResponseComplete(facesContext, "processValidations", false)
|
||||
|| shouldRenderResponse(facesContext, "processValidations",
|
||||
false)) {
|
||||
// since this phase is completed we don't need to return right away
|
||||
// even if the response is completed
|
||||
skipFurtherProcessing = true;
|
||||
}
|
||||
|
||||
if (!skipFurtherProcessing && log.isTraceEnabled())
|
||||
log.trace("exiting processValidations");
|
||||
return skipFurtherProcessing;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright 2006 The Apache Software Foundation.
|
||||
*
|
||||
* 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.apache.struts2.jsf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.faces.FacesException;
|
||||
import javax.faces.application.Application;
|
||||
import javax.faces.application.ViewHandler;
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.component.UIInput;
|
||||
import javax.faces.component.UIViewRoot;
|
||||
import javax.faces.context.ExternalContext;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.el.ValueBinding;
|
||||
import javax.faces.event.PhaseId;
|
||||
|
||||
/**
|
||||
* Restores the view or component tree
|
||||
*/
|
||||
public class RestoreViewInterceptor extends FacesInterceptor {
|
||||
|
||||
private static final long serialVersionUID = -1500785113037140668L;
|
||||
|
||||
/**
|
||||
* Restore View (JSF.2.2.1)
|
||||
*
|
||||
* @param viewId
|
||||
* The view id
|
||||
* @param facesContext
|
||||
* The faces context
|
||||
* @return true, if immediate rendering should occur
|
||||
*/
|
||||
protected boolean executePhase(String viewId, FacesContext facesContext) {
|
||||
boolean skipFurtherProcessing = false;
|
||||
if (log.isTraceEnabled())
|
||||
log.trace("entering restoreView");
|
||||
|
||||
informPhaseListenersBefore(facesContext, PhaseId.RESTORE_VIEW);
|
||||
|
||||
try {
|
||||
if (isResponseComplete(facesContext, "restoreView", true)) {
|
||||
// have to skips this phase
|
||||
return true;
|
||||
}
|
||||
if (shouldRenderResponse(facesContext, "restoreView", true)) {
|
||||
skipFurtherProcessing = true;
|
||||
}
|
||||
|
||||
ExternalContext externalContext = facesContext.getExternalContext();
|
||||
String defaultSuffix = externalContext
|
||||
.getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
|
||||
String suffix = defaultSuffix != null ? defaultSuffix
|
||||
: ViewHandler.DEFAULT_SUFFIX;
|
||||
if (viewId != null) {
|
||||
viewId += suffix;
|
||||
}
|
||||
|
||||
if (viewId == null) {
|
||||
if (!externalContext.getRequestServletPath().endsWith("/")) {
|
||||
try {
|
||||
externalContext.redirect(externalContext
|
||||
.getRequestServletPath()
|
||||
+ "/");
|
||||
facesContext.responseComplete();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
throw new FacesException("redirect failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Application application = facesContext.getApplication();
|
||||
ViewHandler viewHandler = application.getViewHandler();
|
||||
|
||||
// boolean viewCreated = false;
|
||||
UIViewRoot viewRoot = viewHandler.restoreView(facesContext, viewId);
|
||||
if (viewRoot == null) {
|
||||
viewRoot = viewHandler.createView(facesContext, viewId);
|
||||
viewRoot.setViewId(viewId);
|
||||
facesContext.renderResponse();
|
||||
// viewCreated = true;
|
||||
}
|
||||
|
||||
facesContext.setViewRoot(viewRoot);
|
||||
|
||||
/*
|
||||
* This section has been disabled because it causes some bug. Be
|
||||
* careful if you need to re-enable it. Furthermore, for an unknown
|
||||
* reason, it seems that by default it is executed (i.e.
|
||||
* log.isTraceEnabled() is true). Bug example : This traceView
|
||||
* causes DebugUtil.printComponent to print all the attributes of
|
||||
* the view components. And if you have a data table within an
|
||||
* aliasBean, this causes the data table to initialize it's value
|
||||
* attribute while the alias isn't set. So, the value initializes
|
||||
* with an UIData.EMPTY_DATA_MODEL, and not with the aliased one.
|
||||
* But as it's initialized, it will not try to get the value from
|
||||
* the ValueBinding next time it needs to. I expect this to cause
|
||||
* more similar bugs. TODO : Completely remove or be SURE by default
|
||||
* it's not executed, and it has no more side-effects.
|
||||
*
|
||||
* if (log.isTraceEnabled()) { //Note: DebugUtils Logger must also
|
||||
* be in trace level DebugUtils.traceView(viewCreated ? "Newly
|
||||
* created view" : "Restored view"); }
|
||||
*/
|
||||
|
||||
if (facesContext.getExternalContext().getRequestParameterMap()
|
||||
.isEmpty()) {
|
||||
// no POST or query parameters --> set render response flag
|
||||
facesContext.renderResponse();
|
||||
}
|
||||
|
||||
recursivelyHandleComponentReferencesAndSetValid(facesContext,
|
||||
viewRoot);
|
||||
} finally {
|
||||
informPhaseListenersAfter(facesContext, PhaseId.RESTORE_VIEW);
|
||||
}
|
||||
|
||||
if (isResponseComplete(facesContext, "restoreView", false)
|
||||
|| shouldRenderResponse(facesContext, "restoreView", false)) {
|
||||
// since this phase is completed we don't need to return right away
|
||||
// even if the response is completed
|
||||
skipFurtherProcessing = true;
|
||||
}
|
||||
|
||||
if (!skipFurtherProcessing && log.isTraceEnabled())
|
||||
log.trace("exiting restoreView ");
|
||||
return skipFurtherProcessing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk the component tree, executing any component-bindings to reattach
|
||||
* components to their backing beans. Also, any UIInput component is marked
|
||||
* as Valid.
|
||||
* <p>
|
||||
* Note that this method effectively breaks encapsulation; instead of asking
|
||||
* each component to update itself and its children, this method just
|
||||
* reaches into each component. That makes it impossible for any component
|
||||
* to customise its behaviour at this point.
|
||||
* <p>
|
||||
* This has been filed as an issue against the spec. Until this issue is
|
||||
* resolved, we'll add a new marker-interface for components to allow them
|
||||
* to define their interest in handling children bindings themselves.
|
||||
*/
|
||||
protected void recursivelyHandleComponentReferencesAndSetValid(
|
||||
FacesContext facesContext, UIComponent parent) {
|
||||
recursivelyHandleComponentReferencesAndSetValid(facesContext, parent,
|
||||
false);
|
||||
}
|
||||
|
||||
protected void recursivelyHandleComponentReferencesAndSetValid(
|
||||
FacesContext facesContext, UIComponent parent, boolean forceHandle) {
|
||||
Method handleBindingsMethod = getBindingMethod(parent);
|
||||
|
||||
if (handleBindingsMethod != null && !forceHandle) {
|
||||
try {
|
||||
handleBindingsMethod.invoke(parent, new Object[] {});
|
||||
} catch (Throwable th) {
|
||||
log.error(
|
||||
"Exception while invoking handleBindings on component with client-id:"
|
||||
+ parent.getClientId(facesContext), th);
|
||||
}
|
||||
} else {
|
||||
for (Iterator it = parent.getFacetsAndChildren(); it.hasNext();) {
|
||||
UIComponent component = (UIComponent) it.next();
|
||||
|
||||
ValueBinding binding = component.getValueBinding("binding"); // TODO:
|
||||
// constant
|
||||
if (binding != null && !binding.isReadOnly(facesContext)) {
|
||||
binding.setValue(facesContext, component);
|
||||
}
|
||||
|
||||
if (component instanceof UIInput) {
|
||||
((UIInput) component).setValid(true);
|
||||
}
|
||||
|
||||
recursivelyHandleComponentReferencesAndSetValid(facesContext,
|
||||
component);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is all a hack to work around a spec-bug which will be fixed in
|
||||
* JSF2.0
|
||||
*
|
||||
* @param parent
|
||||
* @return true if this component is bindingAware (e.g. aliasBean)
|
||||
*/
|
||||
private static Method getBindingMethod(UIComponent parent) {
|
||||
Class[] clazzes = parent.getClass().getInterfaces();
|
||||
|
||||
for (int i = 0; i < clazzes.length; i++) {
|
||||
Class clazz = clazzes[i];
|
||||
|
||||
if (clazz.getName().indexOf("BindingAware") != -1) {
|
||||
try {
|
||||
return parent.getClass().getMethod("handleBindings",
|
||||
new Class[] {});
|
||||
} catch (NoSuchMethodException e) {
|
||||
// return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright 2006 The Apache Software Foundation.
|
||||
*
|
||||
* 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.apache.struts2.jsf;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.faces.application.NavigationHandler;
|
||||
import javax.faces.context.FacesContext;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.config.entities.ResultConfig;
|
||||
|
||||
/**
|
||||
* Overrides the JFS navigation by delegating the result to handling by the core
|
||||
* result code lookup and execution. If a result cannot be found, the previous
|
||||
* NavigationHandler is called.
|
||||
*/
|
||||
public class StrutsNavigationHandler extends NavigationHandler {
|
||||
|
||||
private NavigationHandler parent;
|
||||
|
||||
/**
|
||||
* Creates the handler
|
||||
*
|
||||
* @param handler The old NavigationHandler to possibly delegate to
|
||||
*/
|
||||
public StrutsNavigationHandler(NavigationHandler handler) {
|
||||
this.parent = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores any outcomes as the result code, failing over to the old
|
||||
* NavigationHandler
|
||||
*
|
||||
* @param facesContext The faces context
|
||||
* @param fromAction The action we are coming from
|
||||
* @param outcome The String return code
|
||||
*/
|
||||
@Override
|
||||
public void handleNavigation(FacesContext facesContext, String fromAction, String outcome) {
|
||||
ActionContext ctx = ActionContext.getContext();
|
||||
if (outcome != null) {
|
||||
ActionConfig config = ctx.getActionInvocation().getProxy().getConfig();
|
||||
Map results = config.getResults();
|
||||
|
||||
ResultConfig resultConfig = null;
|
||||
|
||||
synchronized (config) {
|
||||
try {
|
||||
resultConfig = (ResultConfig) results.get(outcome);
|
||||
} catch (NullPointerException e) {
|
||||
}
|
||||
if (resultConfig == null) {
|
||||
// If no result is found for the given resultCode, try to get a wildcard '*' match.
|
||||
resultConfig = (ResultConfig) results.get("*");
|
||||
}
|
||||
}
|
||||
if (resultConfig != null) {
|
||||
ctx.getActionInvocation().setResultCode(outcome);
|
||||
} else {
|
||||
// Failing over to parent handler
|
||||
parent.handleNavigation(facesContext, fromAction, outcome);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright 2006 The Apache Software Foundation.
|
||||
*
|
||||
* 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.apache.struts2.jsf;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.el.EvaluationException;
|
||||
import javax.faces.el.VariableResolver;
|
||||
|
||||
import com.opensymphony.xwork2.util.OgnlValueStack;
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
|
||||
/**
|
||||
* Will return a reference to the current action if the action name matches the
|
||||
* requested variable name. Otherwise it will attempt to resolve the name from
|
||||
* the value stack. Otherwise it will delegate to the original jsf resolver.
|
||||
*/
|
||||
public class StrutsVariableResolver extends VariableResolver {
|
||||
|
||||
/** The original <code>VariableResolver</code> passed to our constructor. */
|
||||
private VariableResolver original = null;
|
||||
|
||||
/** The variable name of our Struts action */
|
||||
private static final String STRUTS_VARIABLE_NAME = "action";
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param original
|
||||
* Original resolver to delegate to.
|
||||
*/
|
||||
public StrutsVariableResolver(VariableResolver original) {
|
||||
|
||||
this.original = original;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Will return a reference to the current action if the action name matches
|
||||
* the requested variable name. Otherwise it will attempt to resolve the
|
||||
* name from the value stack. Otherwise it will delegate to the original jsf
|
||||
* resolver.
|
||||
* </p>
|
||||
*
|
||||
* @param name
|
||||
* Variable name to be resolved
|
||||
*/
|
||||
public Object resolveVariable(FacesContext context, String name)
|
||||
throws EvaluationException {
|
||||
|
||||
if (STRUTS_VARIABLE_NAME.equals(name)) {
|
||||
return ActionContext.getContext().getActionInvocation().getAction();
|
||||
}
|
||||
|
||||
Object obj = ActionContext.getContext().getValueStack().findValue(name);
|
||||
if (obj != null) {
|
||||
return obj;
|
||||
} else {
|
||||
return original.resolveVariable(context, name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright 2006 The Apache Software Foundation.
|
||||
*
|
||||
* 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.apache.struts2.jsf;
|
||||
|
||||
import javax.faces.FacesException;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.event.PhaseId;
|
||||
|
||||
/**
|
||||
* Updates the model values from the component tree
|
||||
*/
|
||||
public class UpdateModelValuesInterceptor extends FacesInterceptor {
|
||||
|
||||
private static final long serialVersionUID = 4011504235094251077L;
|
||||
|
||||
/**
|
||||
* Update Model Values (JSF.2.2.4)
|
||||
*
|
||||
* @param viewId
|
||||
* The view id
|
||||
* @param facesContext
|
||||
* The faces context
|
||||
* @return true, if response is complete
|
||||
*/
|
||||
protected boolean executePhase(String viewId, FacesContext facesContext)
|
||||
throws FacesException {
|
||||
boolean skipFurtherProcessing = false;
|
||||
if (log.isTraceEnabled())
|
||||
log.trace("entering updateModelValues");
|
||||
|
||||
informPhaseListenersBefore(facesContext, PhaseId.UPDATE_MODEL_VALUES);
|
||||
|
||||
try {
|
||||
if (isResponseComplete(facesContext, "updateModelValues", true)) {
|
||||
// have to return right away
|
||||
return true;
|
||||
}
|
||||
if (shouldRenderResponse(facesContext, "updateModelValues", true)) {
|
||||
skipFurtherProcessing = true;
|
||||
}
|
||||
|
||||
facesContext.getViewRoot().processUpdates(facesContext);
|
||||
} finally {
|
||||
informPhaseListenersAfter(facesContext, PhaseId.UPDATE_MODEL_VALUES);
|
||||
}
|
||||
|
||||
if (isResponseComplete(facesContext, "updateModelValues", false)
|
||||
|| shouldRenderResponse(facesContext, "updateModelValues",
|
||||
false)) {
|
||||
// since this phase is completed we don't need to return right away
|
||||
// even if the response is completed
|
||||
skipFurtherProcessing = true;
|
||||
}
|
||||
|
||||
if (!skipFurtherProcessing && log.isTraceEnabled())
|
||||
log.trace("exiting updateModelValues");
|
||||
|
||||
return skipFurtherProcessing;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<!DOCTYPE struts PUBLIC
|
||||
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
|
||||
"http://struts.apache.org/dtds/struts-2.0.dtd">
|
||||
|
||||
<struts>
|
||||
<package name="jsf-default" extends="struts-default">
|
||||
|
||||
<result-types>
|
||||
<result-type name="jsf" class="org.apache.struts2.jsf.FacesResult" />
|
||||
</result-types>
|
||||
<interceptors>
|
||||
<interceptor class="org.apache.struts2.jsf.FacesSetupInterceptor" name="jsfSetup" />
|
||||
<interceptor class="org.apache.struts2.jsf.RestoreViewInterceptor" name="jsfRestore" />
|
||||
<interceptor class="org.apache.struts2.jsf.ApplyRequestValuesInterceptor" name="jsfApply" />
|
||||
<interceptor class="org.apache.struts2.jsf.ProcessValidationsInterceptor" name="jsfValidate" />
|
||||
<interceptor class="org.apache.struts2.jsf.UpdateModelValuesInterceptor" name="jsfUpdate" />
|
||||
<interceptor class="org.apache.struts2.jsf.InvokeApplicationInterceptor" name="jsfInvoke" />
|
||||
|
||||
<interceptor-stack name="jsfStack">
|
||||
<interceptor-ref name="jsfSetup">
|
||||
<param name="variableResolver">org.apache.struts2.jsf.StrutsVariableResolver</param>
|
||||
<param name="navigationHandler">org.apache.struts2.jsf.StrutsNavigationHandler</param>
|
||||
</interceptor-ref>
|
||||
<interceptor-ref name="jsfRestore" />
|
||||
<interceptor-ref name="jsfApply" />
|
||||
<interceptor-ref name="jsfValidate" />
|
||||
<interceptor-ref name="jsfUpdate" />
|
||||
<interceptor-ref name="jsfInvoke" />
|
||||
</interceptor-stack>
|
||||
</interceptors>
|
||||
|
||||
<default-interceptor-ref name="jsfStack"/>
|
||||
|
||||
</package>
|
||||
|
||||
</struts>
|
||||
@@ -16,6 +16,7 @@
|
||||
<module>pell-multipart</module>
|
||||
<module>jasperreports</module>
|
||||
<module>jfreechart</module>
|
||||
<module>jsf</module>
|
||||
</modules>
|
||||
|
||||
<distributionManagement>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<interceptor name="actionForm-commonsValidation" class="org.apache.struts2.s1.ActionFormValidationInterceptor">
|
||||
<param name="pathnames">/org/apache/struts/validator/validator-rules.xml,/WEB-INF/validation.xml</param>
|
||||
</interceptor>
|
||||
<interceptor-stack name="struts1-default">
|
||||
<interceptor-stack name="struts1Stack">
|
||||
<interceptor-ref name="static-params"/>
|
||||
<interceptor-ref name="scoped-model-driven"/>
|
||||
<interceptor-ref name="model-driven"/>
|
||||
@@ -24,6 +24,7 @@
|
||||
</interceptor-stack>
|
||||
</interceptors>
|
||||
|
||||
<default-interceptor-ref name="struts1Stack"/>
|
||||
</package>
|
||||
|
||||
</struts>
|
||||
|
||||
Reference in New Issue
Block a user