You are viewing a plain text version of this content. The canonical link for it is here.
Posted to pluto-scm@portals.apache.org by as...@apache.org on 2019/03/25 20:53:40 UTC

[portals-pluto] branch master updated: PLUTO-747 Develop the thymeleaf-portlet-api module

This is an automated email from the ASF dual-hosted git repository.

asfgriff pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/portals-pluto.git


The following commit(s) were added to refs/heads/master by this push:
     new 305ae73  PLUTO-747 Develop the thymeleaf-portlet-api module
305ae73 is described below

commit 305ae734c7c7e62ca1f5e1bdecda69344c5cc4e8
Author: Neil Griffin <ne...@gmail.com>
AuthorDate: Mon Mar 25 16:53:17 2019 -0400

    PLUTO-747 Develop the thymeleaf-portlet-api module
---
 pom.xml                                            |  6 ++
 portlet-templating/pom.xml                         | 19 +++++
 portlet-templating/thymeleaf-portlet-api/pom.xml   | 61 ++++++++++++++++
 .../portlet/DefaultTemplateEngineSupplier.java     | 39 +++++++++++
 .../thymeleaf/portlet/PortletMessageResolver.java  | 73 +++++++++++++++++++
 .../thymeleaf/portlet/PortletTemplateResolver.java | 62 +++++++++++++++++
 .../thymeleaf/portlet/TemplateEngineSupplier.java  | 36 ++++++++++
 .../portlet/TemplateLocationSupplier.java          | 33 +++++++++
 .../pluto/thymeleaf/portlet/VariableValidator.java | 38 ++++++++++
 .../thymeleaf/portlet/VariableValidatorBase.java   | 81 ++++++++++++++++++++++
 .../pluto/thymeleaf/portlet/WebContextBase.java    | 76 ++++++++++++++++++++
 11 files changed, 524 insertions(+)

diff --git a/pom.xml b/pom.xml
index 7718581..30e4a3b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -557,6 +557,11 @@ TODO: Check if we need all of them. -->
         </exclusions>
       </dependency>
       <dependency>
+        <groupId>org.thymeleaf</groupId>
+        <artifactId>thymeleaf</artifactId>
+        <version>3.0.11.RELEASE</version>
+      </dependency>
+      <dependency>
         <groupId>commons-io</groupId>
         <artifactId>commons-io</artifactId>
         <version>${commons-io.version}</version>
@@ -765,6 +770,7 @@ TODO: Check if we need all of them. -->
         <module>pluto-ant-tasks</module>
         <module>pluto-site-skin</module>
         <module>portlet-servlet-adapter</module>
+        <module>portlet-templating</module>
         
         <!-- the Technology Compliance Kit -->
         <module>test-base</module>
diff --git a/portlet-templating/pom.xml b/portlet-templating/pom.xml
new file mode 100644
index 0000000..68e4b85
--- /dev/null
+++ b/portlet-templating/pom.xml
@@ -0,0 +1,19 @@
+<?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.portals.pluto</groupId>
+		<artifactId>pluto</artifactId>
+		<version>3.0.2-SNAPSHOT</version>
+		<relativePath>../pom.xml</relativePath>
+	</parent>
+	<artifactId>portlet-templating</artifactId>
+	<packaging>pom</packaging>
+	<name>Portlet Templating</name>
+
+	<modules>
+		<module>thymeleaf-portlet-api</module>
+	</modules>
+
+</project>
diff --git a/portlet-templating/thymeleaf-portlet-api/pom.xml b/portlet-templating/thymeleaf-portlet-api/pom.xml
new file mode 100644
index 0000000..febf8e0
--- /dev/null
+++ b/portlet-templating/thymeleaf-portlet-api/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor 
+   license agreements. See the NOTICE file distributed with this work for additional 
+   information regarding copyright ownership. The ASF licenses this file to 
+   you 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. -->
+<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.portals.pluto</groupId>
+		<artifactId>portlet-templating</artifactId>
+		<version>3.0.2-SNAPSHOT</version>
+		<relativePath>../pom.xml</relativePath>
+	</parent>
+	<artifactId>thymeleaf-portlet-api</artifactId>
+	<name>Thymeleaf Portlet API</name>
+
+	<dependencies>
+		<dependency>
+			<groupId>javax.portlet</groupId>
+			<artifactId>portlet-api</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>javax.servlet</groupId>
+			<artifactId>javax.servlet-api</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.thymeleaf</groupId>
+			<artifactId>thymeleaf</artifactId>
+			<exclusions>
+				<exclusion>
+					<groupId>ognl</groupId>
+					<artifactId>ognl</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<configuration>
+					<source>1.8</source>
+					<target>1.8</target>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>
diff --git a/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/DefaultTemplateEngineSupplier.java b/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/DefaultTemplateEngineSupplier.java
new file mode 100644
index 0000000..7c6daef
--- /dev/null
+++ b/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/DefaultTemplateEngineSupplier.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.pluto.thymeleaf.portlet;
+
+import org.thymeleaf.TemplateEngine;
+
+
+/**
+ * This is a simple implementation of the {@link TemplateEngineSupplier} interface that can be used as a default.
+ *
+ * @author  Neil Griffin
+ */
+public class DefaultTemplateEngineSupplier implements TemplateEngineSupplier {
+
+	private TemplateEngine templateEngine;
+
+	public DefaultTemplateEngineSupplier(TemplateEngine templateEngine) {
+		this.templateEngine = templateEngine;
+	}
+
+	@Override
+	public TemplateEngine get() {
+		return templateEngine;
+	}
+}
diff --git a/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/PortletMessageResolver.java b/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/PortletMessageResolver.java
new file mode 100644
index 0000000..9245065
--- /dev/null
+++ b/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/PortletMessageResolver.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.pluto.thymeleaf.portlet;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import javax.portlet.PortletConfig;
+
+import org.thymeleaf.messageresolver.StandardMessageResolver;
+
+import org.thymeleaf.templateresource.ITemplateResource;
+
+
+/**
+ * This is a convenient utility class that provides Thymeleaf with a way to resolve messages in a portlet application.
+ *
+ * @author  Neil Griffin
+ */
+public class PortletMessageResolver extends StandardMessageResolver {
+
+	private PortletConfig portletConfig;
+
+	public PortletMessageResolver(PortletConfig portletConfig) {
+		this.portletConfig = portletConfig;
+	}
+
+	@Override
+	protected Map<String, String> resolveMessagesForTemplate(String template, ITemplateResource templateResource,
+		Locale locale) {
+
+		Map<String, String> messages = super.resolveMessagesForTemplate(template, templateResource, locale);
+
+		if (messages.isEmpty()) {
+
+			ResourceBundle resourceBundle = portletConfig.getResourceBundle(locale);
+
+			if (resourceBundle != null) {
+				messages = new HashMap<>();
+
+				Enumeration<String> resourceBundleKeys = resourceBundle.getKeys();
+
+				while (resourceBundleKeys.hasMoreElements()) {
+					String key = resourceBundleKeys.nextElement();
+					Object value = resourceBundle.getObject(key);
+
+					if ((key != null) && (value != null)) {
+						messages.put(key, value.toString());
+					}
+				}
+			}
+		}
+
+		return messages;
+	}
+}
diff --git a/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/PortletTemplateResolver.java b/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/PortletTemplateResolver.java
new file mode 100644
index 0000000..e15d690
--- /dev/null
+++ b/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/PortletTemplateResolver.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.pluto.thymeleaf.portlet;
+
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+import org.thymeleaf.IEngineConfiguration;
+
+import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
+
+
+/**
+ * This is a convenient utility class that provides Thymeleaf with a way to resolve templates in a portlet application.
+ *
+ * @author  Neil Griffin
+ */
+public class PortletTemplateResolver extends ServletContextTemplateResolver {
+
+	private TemplateLocationSupplier templateLocationSupplier;
+
+	public PortletTemplateResolver(ServletContext servletContext, TemplateLocationSupplier templateLocationSupplier) {
+		super(servletContext);
+		this.templateLocationSupplier = templateLocationSupplier;
+	}
+
+	@Override
+	protected String computeResourceName(IEngineConfiguration engineConfiguration, String ownerTemplate,
+		String template, String prefix, String suffix, boolean forceSuffix, Map<String, String> templateAliases,
+		Map<String, Object> templateResolutionAttributes) {
+
+		String resourceName = super.computeResourceName(engineConfiguration, ownerTemplate, template, prefix, suffix,
+				forceSuffix, templateAliases, templateResolutionAttributes);
+
+		if (resourceName.startsWith("/")) {
+			return resourceName;
+		}
+
+		String templateLocation = templateLocationSupplier.get();
+
+		if (!templateLocation.endsWith("/")) {
+			templateLocation = templateLocation.concat("/");
+		}
+
+		return templateLocation.concat(resourceName);
+	}
+}
diff --git a/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/TemplateEngineSupplier.java b/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/TemplateEngineSupplier.java
new file mode 100644
index 0000000..d1d6197
--- /dev/null
+++ b/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/TemplateEngineSupplier.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.pluto.thymeleaf.portlet;
+
+import org.thymeleaf.TemplateEngine;
+
+import java.util.function.Supplier;
+
+
+/**
+ * This interface defines a contract for getting a Thymeleaf template engine.
+ *
+ * @author  Neil Griffin
+ */
+@FunctionalInterface
+public interface TemplateEngineSupplier extends Supplier<TemplateEngine> {
+
+	/**
+	 * Returns a Thymeleaf template engine.
+	 */
+	public TemplateEngine get();
+}
diff --git a/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/TemplateLocationSupplier.java b/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/TemplateLocationSupplier.java
new file mode 100644
index 0000000..cc6b852
--- /dev/null
+++ b/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/TemplateLocationSupplier.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.pluto.thymeleaf.portlet;
+
+import java.util.function.Supplier;
+
+/**
+ * This interface defines a contract for getting the location of a Thymeleaf template.
+ *
+ * @author  Neil Griffin
+ */
+@FunctionalInterface
+public interface TemplateLocationSupplier extends Supplier<String>  {
+
+	/**
+	 * Returns the location of a Thymeleaf template.
+	 */
+	public String get();
+}
diff --git a/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/VariableValidator.java b/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/VariableValidator.java
new file mode 100644
index 0000000..6e1d2fe
--- /dev/null
+++ b/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/VariableValidator.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.pluto.thymeleaf.portlet;
+
+/**
+ * Defines a contract for validating {@link org.thymeleaf.context.WebContext} variable names during a portlet request.
+ *
+ * @author  Neil Griffin
+ */
+public interface VariableValidator {
+
+	/**
+	 * Context initialization parameter that defines whether or not standard portlet beans should be added as variables
+	 * to the current {@link org.thymeleaf.context.WebContext}.
+	 */
+	public static final String INCLUDE_STANDARD_BEANS = "org.apache.pluto.thymeleaf.portlet.INCLUDE_STANDARD_BEANS";
+
+	/**
+	 * Determines whether or not the specified variable name is valid for the current portlet request considering the
+	 * specified flags. If valid, then the variable name and value should be added to the associated {@link
+	 * org.thymeleaf.context.WebContext}.
+	 */
+	public boolean isValidName(String name, boolean headerPhase, boolean renderPhase, boolean resourcePhase);
+}
diff --git a/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/VariableValidatorBase.java b/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/VariableValidatorBase.java
new file mode 100644
index 0000000..4d07c3b
--- /dev/null
+++ b/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/VariableValidatorBase.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.pluto.thymeleaf.portlet;
+
+/**
+ * This is a convenient abstract base class that implements the {@link VariableValidator} interface. Sub-classes
+ * typically override the @{@link #isValidName(String, boolean, boolean, boolean)} method in order to add customizations
+ * for different use-cases.
+ *
+ * @author  Neil Griffin
+ */
+public abstract class VariableValidatorBase implements VariableValidator {
+
+	/**
+	 * Called by the {@link #isValidName(String, boolean, boolean, boolean)} method in order to determine whether or not
+	 * standard bean names should be considered as valid names.
+	 */
+	public abstract boolean isIncludeStandardBeans();
+
+	@Override
+	public boolean isValidName(String name, boolean headerPhase, boolean renderPhase, boolean resourcePhase) {
+
+		if (name == null) {
+			return false;
+		}
+
+		name = name.trim();
+
+		if (name.length() == 0) {
+			return false;
+		}
+
+		if (name.equals("actionParams") || name.equals("actionRequest") || name.equals("actionResponse") ||
+				name.equals("eventRequest") || name.equals("eventResponse") || name.equals("stateAwareResponse")) {
+			return false;
+		}
+		else if ((name.equals("clientDataRequest") || name.equals("mutableRenderParams") ||
+					name.equals("resourceParams") || name.equals("resourceRequest") ||
+					name.equals("resourceResponse")) && !resourcePhase) {
+			return false;
+		}
+		else if ((name.equals("headerRequest") || name.equals("headerResponse")) && !headerPhase) {
+			return false;
+		}
+		else if ((name.equals("renderRequest") || name.equals("renderResponse")) && !renderPhase) {
+			return false;
+		}
+		else if (!isIncludeStandardBeans()) {
+
+			if (name.equals("namespace") || name.equals("contextPath")) {
+				return true;
+			}
+			else if (name.equals("clientDataRequest") || name.equals("cookies") || name.equals("locales") ||
+					name.equals("mimeResponse") || name.equals("mutableRenderParams") || name.equals("portletConfig") ||
+					name.equals("portletContext") || name.equals("portletName") || name.equals("portletMode") ||
+					name.equals("portletPreferences") || name.equals("portletRequest") ||
+					name.equals("portletResponse") || name.equals("portletSession") || name.equals("renderParams") ||
+					name.equals("renderRequest") || name.equals("renderResponse") || name.equals("resourceParams") ||
+					name.equals("resourceRequest") || name.equals("resourceResponse") || name.equals("windowId") ||
+					name.equals("windowState")) {
+				return false;
+			}
+		}
+
+		return true;
+	}
+}
diff --git a/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/WebContextBase.java b/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/WebContextBase.java
new file mode 100644
index 0000000..3ffd379
--- /dev/null
+++ b/portlet-templating/thymeleaf-portlet-api/src/main/java/org/apache/pluto/thymeleaf/portlet/WebContextBase.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.pluto.thymeleaf.portlet;
+
+import java.util.Locale;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.thymeleaf.context.IWebContext;
+
+
+/**
+ * This is a convenient abstract base class that partially implements the {@link IWebContext} interface from Thymeleaf.
+ * Sub-classes must implement the {@link #containsVariable(String)}, {@link #getVariableNames()}, and
+ * {@link #getVariable(String)} methods.
+ *
+ * @author  Neil Griffin
+ */
+public abstract class WebContextBase implements IWebContext {
+
+	private final HttpServletRequest httpServletRequest;
+	private final HttpServletResponse httpServletResponse;
+	private final Locale locale;
+	private final ServletContext servletContext;
+
+	public WebContextBase(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
+		ServletContext servletContext, Locale locale) {
+
+		this.httpServletRequest = httpServletRequest;
+		this.httpServletResponse = httpServletResponse;
+		this.locale = locale;
+		this.servletContext = servletContext;
+	}
+
+	@Override
+	public Locale getLocale() {
+		return locale;
+	}
+
+	@Override
+	public HttpServletRequest getRequest() {
+		return httpServletRequest;
+	}
+
+	@Override
+	public HttpServletResponse getResponse() {
+		return httpServletResponse;
+	}
+
+	@Override
+	public ServletContext getServletContext() {
+		return servletContext;
+	}
+
+	@Override
+	public HttpSession getSession() {
+		return httpServletRequest.getSession();
+	}
+}