diff --git a/docs/README.md b/docs/README.md index 00318730996..c85d5c946d5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -168,13 +168,13 @@ below. ## Sections -* [Building / Deploying uPortal](building-and-deploying-uportal.md) -* [Implementing uPortal](implement/README.md) -* [uPortal System Administration](sysadmin/README.md) -* [Developer's Guide](developer/README.md) -* [Supported web browsers](SUPPORTED_BROWSERS.md) -* [Accessibility](ACCESSIBILITY.md) -* [Project Committers](COMMITTERS.md) +1. [Building / Deploying uPortal](building-and-deploying-uportal.md) +2. [Implementing uPortal](implement/README.md) +3. [uPortal System Administration](sysadmin/README.md) +4. [Developer's Guide](developer/README.md) +5. [Supported web browsers](SUPPORTED_BROWSERS.md) +6. [Accessibility](ACCESSIBILITY.md) +7. [Project Committers](COMMITTERS.md) ## External Links diff --git a/docs/implement/README.md b/docs/implement/README.md index cc4f9e6ec3d..cb4845f5bb7 100644 --- a/docs/implement/README.md +++ b/docs/implement/README.md @@ -3,10 +3,11 @@ This section covers topics related to implementing the portal: adding your users, groups, layouts, *etc.* -1. [Authentication](authentication/README.md) -2. [User Attributes](user-attributes/README.md) -3. Groups and Permissions (TBD) -4. Layout Management (TBD) -5. Content (TBD) -6. [Frontend](frontend/README.md) -7. [Security](security.md) +1. uPortal Core Subsystems + 1. [Authentication](authentication/README.md) + 2. [User Attributes](user-attributes/README.md) + 3. Groups and Permissions (TBD) + 4. Layout Management (TBD) + 5. Content (TBD) +2. [Frontend](frontend/README.md) +3. [Security](security.md) diff --git a/docs/implement/frontend/README.md b/docs/implement/frontend/README.md index 808f31d22a3..596269715b3 100644 --- a/docs/implement/frontend/README.md +++ b/docs/implement/frontend/README.md @@ -1,5 +1,8 @@ # Frontend Implementation / Customization uPortal offers a flexible ability to customize the look and feel of the user experience. +## Topics + * [Skinning uPortal](SKINNING_UPORTAL.md) +* [Configuring the uPortal Rendering Pipeline](RENDERING_PIPELINE.md) * [Using Angular](USING_ANGULAR.md) diff --git a/docs/implement/frontend/RENDERING_PIPELINE.md b/docs/implement/frontend/RENDERING_PIPELINE.md new file mode 100644 index 00000000000..054fe13e90e --- /dev/null +++ b/docs/implement/frontend/RENDERING_PIPELINE.md @@ -0,0 +1,190 @@ +# Configuring the uPortal Rendering Pipeline + +uPortal implements rendering of complete pages using a _pipeline_: a nested structure of discrete, +pluggable elements that each implement the same Java interface. The word "pipeline" is suitable +because it invokes the concepts of _movement_ and _throughput_, but in software this design is also +known as the [Decorator Pattern][]. + +The Java interface at the center of the uPortal Rendering Pipeline is `IPortalRenderingPipeline`. +Instances of `IPortalRenderingPipeline` are Spring-managed beans. The primary rendering pipline +bean is assigned an id (in Spring) of `portalRenderingPipeline`. Components in other parts of the +portal (outside the Rendering Pipeline) use this bean (exclusively) to interact with rendering in +the portal. + +The `IPortalRenderingPipeline` interface defines only one method: + +``` java +public void renderState(HttpServletRequest req, HttpServletResponse res) + throws ServletException, IOException; +``` + +## uPortal Standard Pipeline + +The "standard" rendering pipeline is the one that comes with uPortal out-of-the-box: by default, +uPortal 5 uses the same rendering pipeline configuration as uPortal 4.3 -- based on an instance of +`DynamicRenderingPipeline` that contains a number of _components_. + +The components within `DynamicRenderingPipeline` also each implement a single interface: +`CharacterPipelineComponent`, which itself extends +`PipelineComponent`. (The standard rendering pipeline is +hard-wired for XML/XSLT.) Each component implements a discrete step in the rendering of a page +request. + +The standard pipeline includes (as of this writing) the following components (steps): + +1. `analyticsIncorporationComponent` +2. `portletRenderingIncorporationComponent` +3. `portletRenderingInitiationCharacterComponent` +4. `themeCachingComponent` +5. `postSerializerLogger` +6. `staxSerializingComponent` +7. `postThemeTransformLogger` +8. `themeTransformComponent` +9. `preThemeTransformLogger` +10. `themeAttributeIncorporationComponent` +11. `portletRenderingInitiationComponent` +12. `structureCachingComponent` +13. `postStructureTransformLogger` +14. `structureTransformComponent` +15. `preStructureTransformLogger` +16. `structureAttributeIncorporationComponent` +17. `portletWindowAttributeIncorporationComponent` +18. `dashboardWindowStateSettingsStAXComponent` +19. `postUserLayoutStoreLogger` +20. `userLayoutStoreComponent` + +The order of processing for these pipeline components is essentially _backwards_: bottom to top. + +## Using `RenderingPipelineBranchPoint` Beans + +uPortal adopters may configure the Rendering Pipeline to suit their needs. Most common use cases +can be satisfied using `RenderingPipelineBranchPoint` beans. Rendering branch points are Java +objects (Spring-managed beans) that tell some (or all) HTTP requests to follow a different path. +Rendering branch points follow the standard uPortal 5 configuration strategy for Spring-managed +beans: if you supply a properly-configured bean of the correct type (_viz._ +`RenderingPipelineBranchPoint`) to the Spring Application Context, uPortal will _discover_ it and +_do the right thing_. (uPortal will provide it as a dependency to the components that know what +to do with it.) + +uPortal evaluates `RenderingPipelineBranchPoint` beans, if present, in the specified order. If a +branch indicates that it _should_ be followed, it _will_ be followed, and no further branches will +be tested. If no branch is followed, the standard rendering pipeline will be used. + +`RenderingPipelineBranchPoint` beans accept the following configuration settings: + +| Property | Type | Required? | Notes | +| -------- | ---- |:---------:| ----- | +| `order` | `int` | N* | Defines the sequence of branch points when more than one are present (in which case `order` is required). Branches with lower `order` values come before higher values. | +| `predicate` | `java.util.function.Predicate` | Y | If the `predicate` returns `true`, the branch will be followed; otherwise the next branch will be tested. | +| `alternatePipe` | `IPortalRenderingPipeline` | Y | The rendering path that will be followed if the `predicate` returns `true`. | + +### Examples + +The following examples illustrate some typical uses for `RenderingPipelineBranchPoint` beans. Each +of these examples can be configured in +`uPortal-start/overlays/uPortal/src/main/resources/properties/contextOverrides/overridesContext.xml`. + +#### Example 1: Redirect Unauthenticated Users to CAS/Shibboleth + +This example illustrates a commonly-requested feature: disallow unauthenticated access to the +portal. + +``` xml + + + + + + + + + + +``` + +#### Example 2: Integrate uPortal-home + +This example illustrates required uPortal Rendering Pipeline configuration for integration with +[uPortal-home][]. + +``` xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +[Decorator Pattern]: https://en.wikipedia.org/wiki/Decorator_pattern +[uPortal-home]: https://github.com/uPortal-Project/uportal-home diff --git a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/BranchingRenderingPipeline.java b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/BranchingRenderingPipeline.java index e4d4a370bf0..b650ba7d97d 100644 --- a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/BranchingRenderingPipeline.java +++ b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/BranchingRenderingPipeline.java @@ -14,8 +14,8 @@ */ package org.apereo.portal.rendering; -import com.google.common.base.Predicate; import java.io.IOException; +import java.util.function.Predicate; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -30,7 +30,10 @@ * proceed down the false branch. * * @since 4.2 + * @deprecated In favor of {@link RenderingPipelineBranchPoint}, which supports configuration of the + * same components/features in the uP5 style. */ +@Deprecated public class BranchingRenderingPipeline implements IPortalRenderingPipeline { protected final Logger logger = LoggerFactory.getLogger(getClass()); @@ -51,7 +54,7 @@ public void renderState(final HttpServletRequest request, final HttpServletRespo // render either the true or false pipe, depending on the trueness or falseness of the // predicate - if (predicate.apply(request)) { + if (predicate.test(request)) { logger.trace("Branching to the true pipe [{}].", truePipe); truePipe.renderState(request, response); } else { diff --git a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/IPortalRenderingPipeline.java b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/IPortalRenderingPipeline.java index 68d4de6f23b..7e49228f4ee 100644 --- a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/IPortalRenderingPipeline.java +++ b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/IPortalRenderingPipeline.java @@ -22,6 +22,7 @@ /** Describes the entry point into the uPortal rendering pipeline. */ public interface IPortalRenderingPipeline { + /** * renderState method orchestrates the rendering pipeline which includes worker * dispatching, and the rendering process from layout access, to channel rendering, to writing @@ -31,6 +32,6 @@ public interface IPortalRenderingPipeline { * @param res the HttpServletResponse * @exception PortalException if an error occurs */ - public void renderState(HttpServletRequest req, HttpServletResponse res) + void renderState(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException; } diff --git a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/PipelineComponent.java b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/PipelineComponent.java index ba9d6947d19..ab5df444be1 100644 --- a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/PipelineComponent.java +++ b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/PipelineComponent.java @@ -29,9 +29,9 @@ public interface PipelineComponent { /** Get the cache key for the request */ - public CacheKey getCacheKey(HttpServletRequest request, HttpServletResponse response); + CacheKey getCacheKey(HttpServletRequest request, HttpServletResponse response); /** Get the event reader and corresponding cache key for the request */ - public PipelineEventReader getEventReader( + PipelineEventReader getEventReader( HttpServletRequest request, HttpServletResponse response); } diff --git a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/RenderingPipelineBranchPoint.java b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/RenderingPipelineBranchPoint.java new file mode 100644 index 00000000000..8f48421c76b --- /dev/null +++ b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/RenderingPipelineBranchPoint.java @@ -0,0 +1,112 @@ +/** + * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. Apereo + * 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 the + * following location: + * + *

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.apereo.portal.rendering; + +import java.io.IOException; +import java.util.function.Predicate; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Required; + +/** + * Offers an alternative to the "standard" rendering pipeline if certain conditions (encapsulated in + * a Predicate) are satisfied. If the specified Predicate returns + * true, the alternate path will be followed. + * + *

This class is an extension point for uPortal adopters. Beans of this type are automatically + * detected and processed by the uPortal rendering infrastructure. Multiple + * RenderingPipelineBranchPoint beans will be ordered based on their order + * properties. Beans with a lower order score are processed before beans with a higher order. + * + * @since 5.0 + */ +public class RenderingPipelineBranchPoint implements Comparable { + + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + private int order = Integer.MAX_VALUE; // default -- last position + + // dependency-injected + private Predicate predicate; + + // dependency-injected + private IPortalRenderingPipeline alternatePipe; + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + @Required + public void setPredicate(Predicate predicate) { + this.predicate = predicate; + } + + @Required + public void setAlternatePipe(IPortalRenderingPipeline alternatePipe) { + this.alternatePipe = alternatePipe; + } + + /** + * Checks this bean's Predicate to see if the alternate rendering path should be + * followed. If so, it will follow the path and return true; if not, it will + * immediately return false; + * + * @param request The current HttpServletRequest + * @param response The current HttpServletResponse + * @return true if the alternate path was followed, otherwise false + * indicating that the next {@link RenderingPipelineBranchPoint} should be attempted or the + * standard pipeline should be used + */ + public boolean renderStateIfApplicable( + final HttpServletRequest request, final HttpServletResponse response) + throws ServletException, IOException { + + final boolean rslt = predicate.test(request); + if (rslt) { + logger.debug( + "Using alternate pipe [{}] for branch point with order={}", + alternatePipe, + order); + alternatePipe.renderState(request, response); + } else { + logger.debug( + "Bypassing alternate pipe [{}] for branch point with order={}", + alternatePipe, + order); + } + return rslt; + } + + @Override + public String toString() { + return "RenderingPipelineBranchPoint with predicate [" + + predicate + + "] proceeds down pipe [" + + alternatePipe + + "] when the predicate is true."; + } + + @Override + public int compareTo(RenderingPipelineBranchPoint branchPoint) { + return Integer.compare(order, branchPoint.order); + } +} diff --git a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/FocusedOnOnePortletPredicate.java b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/FocusedOnOnePortletPredicate.java index 7b8effdbf1b..22a38927562 100644 --- a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/FocusedOnOnePortletPredicate.java +++ b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/FocusedOnOnePortletPredicate.java @@ -14,7 +14,7 @@ */ package org.apereo.portal.rendering.predicates; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import javax.servlet.http.HttpServletRequest; import org.apereo.portal.url.IPortalRequestInfo; import org.apereo.portal.url.IUrlSyntaxProvider; @@ -37,7 +37,7 @@ public class FocusedOnOnePortletPredicate implements Predicate { private IUserInstanceManager userInstanceManager; @Override - public boolean apply(final HttpServletRequest request) { + public boolean test(final HttpServletRequest request) { final IUserInstance userInstance = this.userInstanceManager.getUserInstance(request); return userInstance.getPerson().isGuest(); diff --git a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/NotGuestUserPredicate.java b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/NotGuestUserPredicate.java index dc53c98505c..cb06e1d8561 100644 --- a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/NotGuestUserPredicate.java +++ b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/NotGuestUserPredicate.java @@ -14,7 +14,7 @@ */ package org.apereo.portal.rendering.predicates; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import javax.servlet.http.HttpServletRequest; import org.apereo.portal.user.IUserInstance; import org.apereo.portal.user.IUserInstanceManager; @@ -27,7 +27,7 @@ public class NotGuestUserPredicate implements Predicate { private IUserInstanceManager userInstanceManager; @Override - public boolean apply(final HttpServletRequest request) { + public boolean test(final HttpServletRequest request) { final IUserInstance userInstance = this.userInstanceManager.getUserInstance(request); return !userInstance.getPerson().isGuest(); diff --git a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/ProfileFNamePredicate.java b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/ProfileFNamePredicate.java index 7f94f6c51d5..93c72a23d7b 100644 --- a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/ProfileFNamePredicate.java +++ b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/ProfileFNamePredicate.java @@ -14,7 +14,7 @@ */ package org.apereo.portal.rendering.predicates; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import javax.servlet.http.HttpServletRequest; import org.apereo.portal.user.IUserInstance; import org.apereo.portal.user.IUserInstanceManager; @@ -41,7 +41,7 @@ public class ProfileFNamePredicate implements Predicate { private String profileFNameToMatch; @Override - public boolean apply(final HttpServletRequest request) { + public boolean test(final HttpServletRequest request) { final IUserInstance userInstance = this.userInstanceManager.getUserInstance(request); diff --git a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/RenderOnWebFlagSet.java b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/RenderOnWebFlagSetPredicate.java similarity index 93% rename from uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/RenderOnWebFlagSet.java rename to uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/RenderOnWebFlagSetPredicate.java index 1345ca55c8f..b46ba226071 100644 --- a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/RenderOnWebFlagSet.java +++ b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/RenderOnWebFlagSetPredicate.java @@ -14,8 +14,8 @@ */ package org.apereo.portal.rendering.predicates; -import com.google.common.base.Predicate; import java.util.Iterator; +import java.util.function.Predicate; import javax.servlet.http.HttpServletRequest; import org.apereo.portal.portlet.om.IPortletDefinition; import org.apereo.portal.portlet.om.IPortletPreference; @@ -24,7 +24,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -public class RenderOnWebFlagSet implements Predicate { +public class RenderOnWebFlagSetPredicate implements Predicate { protected final Logger logger = LoggerFactory.getLogger(getClass()); @@ -36,7 +36,7 @@ public void setUtils(RequestRenderingPipelineUtils u) { } @Override - public boolean apply(final HttpServletRequest request) { + public boolean test(final HttpServletRequest request) { try { final IPortletDefinition portletDefinition = utils.getPortletDefinitionFromServletRequest(request); diff --git a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/URLInSpecificState.java b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/URLInSpecificStatePredicate.java similarity index 93% rename from uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/URLInSpecificState.java rename to uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/URLInSpecificStatePredicate.java index 8166e39bdfc..0a87213a331 100644 --- a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/URLInSpecificState.java +++ b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/predicates/URLInSpecificStatePredicate.java @@ -14,7 +14,7 @@ */ package org.apereo.portal.rendering.predicates; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import javax.servlet.http.HttpServletRequest; import org.apereo.portal.url.IPortalRequestInfo; import org.apereo.portal.url.IUrlSyntaxProvider; @@ -29,7 +29,7 @@ * * @since 4.2 */ -public class URLInSpecificState implements Predicate { +public class URLInSpecificStatePredicate implements Predicate { protected final Logger logger = LoggerFactory.getLogger(getClass()); @@ -40,7 +40,7 @@ public class URLInSpecificState implements Predicate { private boolean isNegated; @Override - public boolean apply(final HttpServletRequest request) { + public boolean test(final HttpServletRequest request) { final IPortalRequestInfo portalRequestInfo = this.urlSyntaxProvider.getPortalRequestInfo(request); diff --git a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/xslt/StaticTransformerConfigurationSource.java b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/xslt/StaticTransformerConfigurationSource.java index 96094844f65..ceb5dbad2e4 100644 --- a/uPortal-rendering/src/main/java/org/apereo/portal/rendering/xslt/StaticTransformerConfigurationSource.java +++ b/uPortal-rendering/src/main/java/org/apereo/portal/rendering/xslt/StaticTransformerConfigurationSource.java @@ -15,10 +15,12 @@ package org.apereo.portal.rendering.xslt; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Properties; import java.util.Set; +import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apereo.portal.spring.spel.IPortalSpELService; @@ -36,7 +38,8 @@ public class StaticTransformerConfigurationSource implements TransformerConfigur private Properties outputProperties; private LinkedHashMap parameters; private IPortalSpELService portalSpELService; - private Map parameterExpressions; + private final Map unparsedParameterExpressions = new HashMap<>(); + private final Map parameterExpressions = new LinkedHashMap<>(); private Set cacheKeyExcludedParameters = Collections.emptySet(); @Autowired @@ -49,20 +52,11 @@ public void setProperties(Properties transformerOutputProperties) { } public void setParameters(Map transformerParameters) { - this.parameters = new LinkedHashMap(transformerParameters); + this.parameters = new LinkedHashMap<>(transformerParameters); } public void setParameterExpressions(Map parameterExpressions) { - final Map parameterExpressionsBuilder = - new LinkedHashMap(); - - for (final Map.Entry expressionEntry : parameterExpressions.entrySet()) { - final String string = expressionEntry.getValue(); - final Expression expression = this.portalSpELService.parseExpression(string); - parameterExpressionsBuilder.put(expressionEntry.getKey(), expression); - } - - this.parameterExpressions = parameterExpressionsBuilder; + unparsedParameterExpressions.putAll(parameterExpressions); } /** Parameter keys to exclude from the cache key. */ @@ -70,6 +64,16 @@ public void setCacheKeyExcludedParameters(Set cacheKeyExcludedParameters this.cacheKeyExcludedParameters = cacheKeyExcludedParameters; } + @PostConstruct + public void init() { + for (final Map.Entry expressionEntry : + unparsedParameterExpressions.entrySet()) { + final String string = expressionEntry.getValue(); + final Expression expression = portalSpELService.parseExpression(string); + parameterExpressions.put(expressionEntry.getKey(), expression); + } + } + @Override public CacheKey getCacheKey(HttpServletRequest request, HttpServletResponse response) { final LinkedHashMap transformerParameters = @@ -92,19 +96,16 @@ public LinkedHashMap getParameters( final ServletWebRequest webRequest = new ServletWebRequest(request, response); // Clone the static parameter map - final LinkedHashMap parameters = - new LinkedHashMap(this.parameters); + final LinkedHashMap parameters = new LinkedHashMap<>(this.parameters); // Add in any SpEL based parameters - if (this.parameterExpressions != null) { - for (final Map.Entry expressionEntry : - this.parameterExpressions.entrySet()) { - final Expression expression = expressionEntry.getValue(); - final Object value = this.portalSpELService.getValue(expression, webRequest); - - if (value != null) { - parameters.put(expressionEntry.getKey(), value); - } + for (final Map.Entry expressionEntry : + this.parameterExpressions.entrySet()) { + final Expression expression = expressionEntry.getValue(); + final Object value = this.portalSpELService.getValue(expression, webRequest); + + if (value != null) { + parameters.put(expressionEntry.getKey(), value); } } diff --git a/uPortal-webapp/src/test/java/org/apereo/portal/rendering/RedirectRenderingPipelineTerminatorTest.java b/uPortal-rendering/src/test/java/org/apereo/portal/rendering/RedirectRenderingPipelineTerminatorTest.java similarity index 100% rename from uPortal-webapp/src/test/java/org/apereo/portal/rendering/RedirectRenderingPipelineTerminatorTest.java rename to uPortal-rendering/src/test/java/org/apereo/portal/rendering/RedirectRenderingPipelineTerminatorTest.java diff --git a/uPortal-webapp/src/test/java/org/apereo/portal/rendering/BranchingRenderingPipelineTest.java b/uPortal-rendering/src/test/java/org/apereo/portal/rendering/RenderingPipelineBranchPointTest.java similarity index 59% rename from uPortal-webapp/src/test/java/org/apereo/portal/rendering/BranchingRenderingPipelineTest.java rename to uPortal-rendering/src/test/java/org/apereo/portal/rendering/RenderingPipelineBranchPointTest.java index aa56a8dd76f..9cbe5b34c88 100644 --- a/uPortal-webapp/src/test/java/org/apereo/portal/rendering/BranchingRenderingPipelineTest.java +++ b/uPortal-rendering/src/test/java/org/apereo/portal/rendering/RenderingPipelineBranchPointTest.java @@ -18,8 +18,8 @@ import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; -import com.google.common.base.Predicate; import java.io.IOException; +import java.util.function.Predicate; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -28,33 +28,30 @@ import org.mockito.Mock; /** - * Unit tests for BranchingRedneringPipeline. + * Unit tests for RenderingPipelineBranchPoint. * - * @since 4.2 + * @since 5.0 */ -public class BranchingRenderingPipelineTest { +public class RenderingPipelineBranchPointTest { @Mock private HttpServletRequest request; @Mock private HttpServletResponse response; - @Mock private IPortalRenderingPipeline truePipe; + @Mock private IPortalRenderingPipeline alternatePipe; - @Mock private IPortalRenderingPipeline falsePipe; + @Mock private Predicate predicate; - @Mock private Predicate predicate; - - private BranchingRenderingPipeline branchingRenderingPipeline; + private RenderingPipelineBranchPoint renderingPipelineBranchPoint; @Before public void setUp() throws Exception { initMocks(this); - branchingRenderingPipeline = new BranchingRenderingPipeline(); - branchingRenderingPipeline.setTruePipe(truePipe); - branchingRenderingPipeline.setFalsePipe(falsePipe); - branchingRenderingPipeline.setPredicate(predicate); + renderingPipelineBranchPoint = new RenderingPipelineBranchPoint(); + renderingPipelineBranchPoint.setPredicate(predicate); + renderingPipelineBranchPoint.setAlternatePipe(alternatePipe); } /** @@ -66,13 +63,12 @@ public void setUp() throws Exception { @Test public void rendersTruePipeWhenPredicateIsTrue() throws ServletException, IOException { - when(predicate.apply(request)).thenReturn(true); - - branchingRenderingPipeline.renderState(request, response); + when(predicate.test(request)).thenReturn(true); - verify(truePipe).renderState(request, response); + boolean outcome = renderingPipelineBranchPoint.renderStateIfApplicable(request, response); - verifyZeroInteractions(falsePipe); + assertTrue("Expected outcome == true", outcome); + verify(alternatePipe).renderState(request, response); } /** @@ -84,29 +80,26 @@ public void rendersTruePipeWhenPredicateIsTrue() throws ServletException, IOExce @Test public void rendersFalsePipeWhenPredicateIsFalse() throws ServletException, IOException { - when(predicate.apply(request)).thenReturn(false); - - branchingRenderingPipeline.renderState(request, response); + when(predicate.test(request)).thenReturn(false); - verify(falsePipe).renderState(request, response); + boolean outcome = renderingPipelineBranchPoint.renderStateIfApplicable(request, response); - verifyZeroInteractions(truePipe); + assertFalse("Expected outcome == true", outcome); + verifyZeroInteractions(alternatePipe); } - /** Test that BranchingRenderingPipeline has a friendly toString() implementation. */ + /** Test that RenderingPipelineBranchPoint has a friendly toString() implementation. */ @Test public void hasFriendlyToString() { when(predicate.toString()).thenReturn("String representation of the predicate."); - when(truePipe.toString()).thenReturn("String representation of truePipe."); - when(falsePipe.toString()).thenReturn("String representation of falsePipe."); + when(alternatePipe.toString()).thenReturn("String representation of alternatePipe."); final String friendlyToString = - "BranchingRenderingPipeline which considering predicate " + "RenderingPipelineBranchPoint with predicate " + "[String representation of the predicate.]" - + " proceeds down pipe [String representation of truePipe.] when the predicate is true" - + " and proceeds down pipe [String representation of falsePipe.] when the predicate is false."; + + " proceeds down pipe [String representation of alternatePipe.] when the predicate is true."; - assertEquals(friendlyToString, branchingRenderingPipeline.toString()); + assertEquals(friendlyToString, renderingPipelineBranchPoint.toString()); } } diff --git a/uPortal-webapp/src/test/java/org/apereo/portal/rendering/predicates/FocusedOnOnePortletPredicateTest.java b/uPortal-rendering/src/test/java/org/apereo/portal/rendering/predicates/FocusedOnOnePortletPredicateTest.java similarity index 93% rename from uPortal-webapp/src/test/java/org/apereo/portal/rendering/predicates/FocusedOnOnePortletPredicateTest.java rename to uPortal-rendering/src/test/java/org/apereo/portal/rendering/predicates/FocusedOnOnePortletPredicateTest.java index 6113886eae5..1d03a10f636 100644 --- a/uPortal-webapp/src/test/java/org/apereo/portal/rendering/predicates/FocusedOnOnePortletPredicateTest.java +++ b/uPortal-rendering/src/test/java/org/apereo/portal/rendering/predicates/FocusedOnOnePortletPredicateTest.java @@ -65,7 +65,7 @@ public void falseWhenAddressesDashboard() { predicate.setUrlSyntaxProvider(this.syntaxProvider); - assertFalse(predicate.apply(this.mockRequest)); + assertFalse(predicate.test(this.mockRequest)); } /** @@ -79,7 +79,7 @@ public void trueWhenAddressesSpecificPortletMaximized() { .thenReturn(this.portalRequestInfo); when(this.portalRequestInfo.getUrlState()).thenReturn(UrlState.MAX); - assertTrue(predicate.apply(this.mockRequest)); + assertTrue(predicate.test(this.mockRequest)); } /** @@ -93,7 +93,7 @@ public void trueWhenAddressesSpecificPortletExclusive() { .thenReturn(this.portalRequestInfo); when(this.portalRequestInfo.getUrlState()).thenReturn(UrlState.EXCLUSIVE); - assertTrue(predicate.apply(this.mockRequest)); + assertTrue(predicate.test(this.mockRequest)); } /** @@ -107,7 +107,7 @@ public void trueWhenAddressesSpecificPortletDetached() { .thenReturn(this.portalRequestInfo); when(this.portalRequestInfo.getUrlState()).thenReturn(UrlState.DETACHED); - assertTrue(predicate.apply(this.mockRequest)); + assertTrue(predicate.test(this.mockRequest)); } /** @@ -119,7 +119,7 @@ public void falseWhenPortalRequestInfoNull() { when(this.syntaxProvider.getPortalRequestInfo(mockRequest)).thenReturn(null); - assertFalse(predicate.apply(this.mockRequest)); + assertFalse(predicate.test(this.mockRequest)); } @Test diff --git a/uPortal-webapp/src/test/java/org/apereo/portal/rendering/predicates/ProfileFNamePredicateTest.java b/uPortal-rendering/src/test/java/org/apereo/portal/rendering/predicates/ProfileFNamePredicateTest.java similarity index 97% rename from uPortal-webapp/src/test/java/org/apereo/portal/rendering/predicates/ProfileFNamePredicateTest.java rename to uPortal-rendering/src/test/java/org/apereo/portal/rendering/predicates/ProfileFNamePredicateTest.java index 9f4c8cc2da7..2ff55128cd8 100644 --- a/uPortal-webapp/src/test/java/org/apereo/portal/rendering/predicates/ProfileFNamePredicateTest.java +++ b/uPortal-rendering/src/test/java/org/apereo/portal/rendering/predicates/ProfileFNamePredicateTest.java @@ -86,7 +86,7 @@ public void whenProfileNameMatchesReturnsTrue() { // configure to look for the profile fname that will be found predicate.setProfileFNameToMatch("exampleUserProfileFname"); - assertTrue(predicate.apply(request)); + assertTrue(predicate.test(request)); } /** @@ -99,7 +99,7 @@ public void whenProfileNameDoesNotMatchReturnsFalse() { // configure to look for a profile fname that will not be found. predicate.setProfileFNameToMatch("willNotFindThis"); - assertFalse(predicate.apply(request)); + assertFalse(predicate.test(request)); } @Test diff --git a/uPortal-utils/uPortal-utils-core/src/main/java/org/apereo/portal/character/stream/CharacterEventReader.java b/uPortal-utils/uPortal-utils-core/src/main/java/org/apereo/portal/character/stream/CharacterEventReader.java index ef5adce9dc4..d33ce110785 100644 --- a/uPortal-utils/uPortal-utils-core/src/main/java/org/apereo/portal/character/stream/CharacterEventReader.java +++ b/uPortal-utils/uPortal-utils-core/src/main/java/org/apereo/portal/character/stream/CharacterEventReader.java @@ -23,11 +23,11 @@ public interface CharacterEventReader extends Iterator { * Check the next XMLEvent without reading it from the stream. Returns null if the stream is at * EOF or has no more XMLEvents. A call to peek() will be equal to the next return of next(). */ - public CharacterEvent peek(); + CharacterEvent peek(); /** * Frees any resources associated with this Reader. This method does not close the underlying * input source. */ - public void close(); + void close(); } diff --git a/uPortal-utils/uPortal-utils-core/src/main/java/org/apereo/portal/character/stream/events/CharacterEvent.java b/uPortal-utils/uPortal-utils-core/src/main/java/org/apereo/portal/character/stream/events/CharacterEvent.java index 13fd9a09b11..6b8e095b0ce 100644 --- a/uPortal-utils/uPortal-utils-core/src/main/java/org/apereo/portal/character/stream/events/CharacterEvent.java +++ b/uPortal-utils/uPortal-utils-core/src/main/java/org/apereo/portal/character/stream/events/CharacterEvent.java @@ -18,5 +18,5 @@ /** */ public interface CharacterEvent extends Serializable { - public CharacterEventTypes getEventType(); + CharacterEventTypes getEventType(); } diff --git a/uPortal-web/src/main/java/org/apereo/portal/rendering/AnalyticsIncorporationComponent.java b/uPortal-web/src/main/java/org/apereo/portal/rendering/AnalyticsIncorporationComponent.java index 34e0e90bb50..93ae9738606 100644 --- a/uPortal-web/src/main/java/org/apereo/portal/rendering/AnalyticsIncorporationComponent.java +++ b/uPortal-web/src/main/java/org/apereo/portal/rendering/AnalyticsIncorporationComponent.java @@ -57,7 +57,7 @@ public class AnalyticsIncorporationComponent extends CharacterPipelineComponentW @JsonFilter(PortletRenderExecutionEventFilterMixIn.FILTER_NAME) private interface PortletRenderExecutionEventFilterMixIn { - static final String FILTER_NAME = "PortletRenderExecutionEventFilter"; + String FILTER_NAME = "PortletRenderExecutionEventFilter"; } // Ignored until https://github.com/FasterXML/jackson-databind/issues/245 is fixed @@ -128,14 +128,12 @@ public PipelineEventReader getEventReader( new AnalyticsIncorporatingEventReader(eventReader, request, startTime); final Map outputProperties = pipelineEventReader.getOutputProperties(); - return new PipelineEventReaderImpl( - portletIncorporatingEventReader, outputProperties); + return new PipelineEventReaderImpl<>(portletIncorporatingEventReader, outputProperties); } protected String serializePortletRenderExecutionEvents(final Set portalEvents) { // Filter to include just portlet render events - final Map renderEvents = - new HashMap(); + final Map renderEvents = new HashMap<>(); for (final PortalEvent portalEvent : portalEvents) { if (portalEvent instanceof PortletRenderExecutionEvent) { final PortletRenderExecutionEvent portletRenderEvent = @@ -173,7 +171,7 @@ protected String serializePortletRenderExecutionEvents(final Set po } protected String serializePageData(HttpServletRequest request, long startTime) { - final Map pageData = new HashMap(); + final Map pageData = new HashMap<>(); pageData.put("executionTimeNano", System.nanoTime() - startTime); final IPortalRequestInfo portalRequestInfo = diff --git a/uPortal-webapp/src/main/java/org/apereo/portal/persondir/AdopterDataSourcesIncorporator.java b/uPortal-webapp/src/main/java/org/apereo/portal/context/persondir/AdopterDataSourcesIncorporator.java similarity index 99% rename from uPortal-webapp/src/main/java/org/apereo/portal/persondir/AdopterDataSourcesIncorporator.java rename to uPortal-webapp/src/main/java/org/apereo/portal/context/persondir/AdopterDataSourcesIncorporator.java index 131aa680608..e18ed8c4149 100644 --- a/uPortal-webapp/src/main/java/org/apereo/portal/persondir/AdopterDataSourcesIncorporator.java +++ b/uPortal-webapp/src/main/java/org/apereo/portal/context/persondir/AdopterDataSourcesIncorporator.java @@ -12,7 +12,7 @@ * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ -package org.apereo.portal.persondir; +package org.apereo.portal.context.persondir; import java.util.ArrayList; import java.util.HashSet; diff --git a/uPortal-webapp/src/main/java/org/apereo/portal/persondir/PersonDirectoryConfiguration.java b/uPortal-webapp/src/main/java/org/apereo/portal/context/persondir/PersonDirectoryConfiguration.java similarity index 98% rename from uPortal-webapp/src/main/java/org/apereo/portal/persondir/PersonDirectoryConfiguration.java rename to uPortal-webapp/src/main/java/org/apereo/portal/context/persondir/PersonDirectoryConfiguration.java index d7fc9069627..2883ed1b034 100644 --- a/uPortal-webapp/src/main/java/org/apereo/portal/persondir/PersonDirectoryConfiguration.java +++ b/uPortal-webapp/src/main/java/org/apereo/portal/context/persondir/PersonDirectoryConfiguration.java @@ -12,7 +12,7 @@ * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ -package org.apereo.portal.persondir; +package org.apereo.portal.context.persondir; import java.util.ArrayList; import java.util.Collections; @@ -28,6 +28,10 @@ import javax.servlet.Filter; import javax.sql.DataSource; import net.sf.ehcache.Cache; +import org.apereo.portal.persondir.ILocalAccountDao; +import org.apereo.portal.persondir.ImpersonationStatusPersonAttributeDao; +import org.apereo.portal.persondir.LocalAccountPersonAttributeDao; +import org.apereo.portal.persondir.PortalRootPersonAttributeDao; import org.apereo.portal.persondir.support.PersonManagerCurrentUserProvider; import org.apereo.portal.utils.cache.MapCacheProvider; import org.apereo.portal.utils.cache.PersonDirectoryCacheKeyGenerator; diff --git a/uPortal-webapp/src/main/java/org/apereo/portal/context/rendering/RenderingPipelineConfiguration.java b/uPortal-webapp/src/main/java/org/apereo/portal/context/rendering/RenderingPipelineConfiguration.java new file mode 100644 index 00000000000..37466e41200 --- /dev/null +++ b/uPortal-webapp/src/main/java/org/apereo/portal/context/rendering/RenderingPipelineConfiguration.java @@ -0,0 +1,613 @@ +/** + * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. Apereo + * 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 the + * following location: + * + *

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.apereo.portal.context.rendering; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.Resource; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import net.sf.ehcache.Cache; +import org.apereo.portal.character.stream.CharacterEventSource; +import org.apereo.portal.character.stream.PortletContentPlaceholderEventSource; +import org.apereo.portal.character.stream.PortletHeaderPlaceholderEventSource; +import org.apereo.portal.character.stream.PortletHelpPlaceholderEventSource; +import org.apereo.portal.character.stream.PortletLinkPlaceholderEventSource; +import org.apereo.portal.character.stream.PortletNewItemCountPlaceholderEventSource; +import org.apereo.portal.character.stream.PortletTitlePlaceholderEventSource; +import org.apereo.portal.character.stream.events.ChunkPointPlaceholderEventSource; +import org.apereo.portal.layout.IUserLayoutManager; +import org.apereo.portal.rendering.AnalyticsIncorporationComponent; +import org.apereo.portal.rendering.CharacterPipelineComponent; +import org.apereo.portal.rendering.DynamicRenderingPipeline; +import org.apereo.portal.rendering.IPortalRenderingPipeline; +import org.apereo.portal.rendering.LoggingCharacterComponent; +import org.apereo.portal.rendering.LoggingStAXComponent; +import org.apereo.portal.rendering.PageAnalyticsDataPlaceholderEventSource; +import org.apereo.portal.rendering.PortletAnalyticsDataPlaceholderEventSource; +import org.apereo.portal.rendering.PortletRenderingIncorporationComponent; +import org.apereo.portal.rendering.PortletRenderingInitiationCharacterComponent; +import org.apereo.portal.rendering.PortletRenderingInitiationStAXComponent; +import org.apereo.portal.rendering.PortletWindowAttributeSource; +import org.apereo.portal.rendering.RenderingPipelineBranchPoint; +import org.apereo.portal.rendering.RenderingPipelineConfigurationException; +import org.apereo.portal.rendering.StAXAttributeIncorporationComponent; +import org.apereo.portal.rendering.StAXPipelineComponent; +import org.apereo.portal.rendering.StAXPipelineComponentWrapper; +import org.apereo.portal.rendering.StAXSerializingComponent; +import org.apereo.portal.rendering.StructureAttributeSource; +import org.apereo.portal.rendering.ThemeAttributeSource; +import org.apereo.portal.rendering.UserLayoutStoreComponent; +import org.apereo.portal.rendering.WindowStateSettingsStAXComponent; +import org.apereo.portal.rendering.cache.CachingCharacterPipelineComponent; +import org.apereo.portal.rendering.cache.CachingStAXPipelineComponent; +import org.apereo.portal.rendering.xslt.LocaleTransformerConfigurationSource; +import org.apereo.portal.rendering.xslt.MergingTransformerConfigurationSource; +import org.apereo.portal.rendering.xslt.StaticTransformerConfigurationSource; +import org.apereo.portal.rendering.xslt.StructureStylesheetDescriptorTransformerConfigurationSource; +import org.apereo.portal.rendering.xslt.StructureStylesheetUserPreferencesTransformerConfigurationSource; +import org.apereo.portal.rendering.xslt.StructureTransformerSource; +import org.apereo.portal.rendering.xslt.ThemeStylesheetDescriptorTransformerConfigurationSource; +import org.apereo.portal.rendering.xslt.ThemeStylesheetUserPreferencesTransformerConfigurationSource; +import org.apereo.portal.rendering.xslt.ThemeTransformerSource; +import org.apereo.portal.rendering.xslt.TransformerConfigurationSource; +import org.apereo.portal.rendering.xslt.TransformerSource; +import org.apereo.portal.rendering.xslt.UserImpersonationTransformerConfigurationSource; +import org.apereo.portal.rendering.xslt.XSLTComponent; +import org.apereo.portal.url.xml.XsltPortalUrlProvider; +import org.apereo.portal.web.skin.ResourcesElementsXsltcHelper; +import org.jasig.resourceserver.aggr.ResourcesDao; +import org.jasig.resourceserver.aggr.ResourcesDaoImpl; +import org.jasig.resourceserver.utils.aggr.ResourcesElementsProvider; +import org.jasig.resourceserver.utils.aggr.ResourcesElementsProviderImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * This @Configuration class sets up (roughly) the same beans that renderingPipelineContext.xml did + * in uP4. + * + * @since 5.0 + */ +@Configuration +public class RenderingPipelineConfiguration { + + private static final String POST_USER_LAYOUT_STORE_LOGGER_NAME = + "org.apereo.portal.rendering.LoggingStAXComponent.POST_LAYOUT"; + private static final String POST_USER_LAYOUT_STORE_LOGGER_STEP_IDENTIFIER = + "postUserLayoutStoreLogger"; + + private static final String PRE_STRUCTURE_TRANSFORM_LOGGER_NAME = + "org.apereo.portal.rendering.LoggingStAXComponent.PRE_STRUCTURE"; + private static final String PRE_STRUCTURE_TRANSFORM_LOGGER_STEP_IDENTIFIER = + "preStructureTransformLogger"; + + private static final String POST_STRUCTURE_TRANSFORM_LOGGER_NAME = + "org.apereo.portal.rendering.LoggingStAXComponent.POST_STRUCTURE"; + private static final String POST_STRUCTURE_TRANSFORM_LOGGER_STEP_IDENTIFIER = + "postStructureTransformLogger"; + + private static final String PRE_THEME_TRANSFORM_LOGGER_NAME = + "org.apereo.portal.rendering.LoggingStAXComponent.PRE_THEME"; + private static final String PRE_THEME_TRANSFORM_LOGGER_STEP_IDENTIFIER = + "preThemeTransformLogger"; + + private static final String POST_THEME_TRANSFORM_LOGGER_NAME = + "org.apereo.portal.rendering.LoggingStAXComponent.POST_THEME"; + private static final String POST_THEME_TRANSFORM_LOGGER_STEP_IDENTIFIER = + "postThemeTransformLogger"; + + private static final String PORTLET_TITLE_PATTERN = "\\{up-portlet-title\\(([^\\)]+)\\)\\}"; + private static final String PORTLET_HELP_PATTERN = "\\{up-portlet-help\\(([^\\)]+)\\)\\}"; + private static final String PORTLET_NEW_ITEM_COUNT_PATTERN = + "\\{up-portlet-new-item-count\\(([^\\)]+)\\)\\}"; + private static final String PORTLET_LINK_PATTERN = + "\\{up-portlet-link\\(([^,]+),([^\\)]+)\\)\\}"; + + @Value("${org.apereo.portal.version}") + private String uPortalVersion; + + @Resource(name = "org.apereo.portal.rendering.STRUCTURE_TRANSFORM") + private Cache structureTransformCache; + + @Autowired private XsltPortalUrlProvider xslPortalUrlProvider; + + @Value("${org.apereo.portal.channels.CLogin.CasLoginUrl}") + private String casLoginUrl; + + @Value("${org.apereo.portal.layout.useTabGroups}") + private String useTabGroups; + + @Value("${org.apereo.portal.layout.useFlyoutMenus:false}") + private String useFlyoutMenus; + + @Resource(name = "org.apereo.portal.rendering.THEME_TRANSFORM") + private Cache themeTransformCache; + + @Autowired(required = false) + private List branchPoints; + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + /** + * This bean is the entry point into the uPortal Rendering Pipeline. It supports {@link + * RenderingPipelineBranchPoint} beans, which are an extension point for adopters. + */ + @Bean(name = "portalRenderingPipeline") + @Qualifier(value = "main") + public IPortalRenderingPipeline getPortalRenderingPipeline() { + + // Rendering Pipeline Branches (adopter extension point) + final List sortedList = + (branchPoints != null) ? new LinkedList<>(branchPoints) : Collections.emptyList(); + Collections.sort(sortedList); + final List branches = + Collections.unmodifiableList(sortedList); + + /* + * Sanity check: if you have multiple RenderingPipelineBranchPoint beans, you can specify + * an 'order' property on some or all of them to control the sequence of processing. + * Having 2 RenderingPipelineBranchPoint beans with the same order value will produce + * non-deterministic results and is a likely source of misconfiguration. + */ + final Set usedOderValues = new HashSet<>(); + boolean hasCollision = + branches.stream() + .anyMatch( + branchPoint -> { + final boolean rslt = + usedOderValues.contains(branchPoint.getOrder()); + usedOderValues.add(branchPoint.getOrder()); + return rslt; + }); + if (hasCollision) { + throw new RenderingPipelineConfigurationException( + "Multiple RenderingPipelineBranchPoint beans have the same 'order' value, which likely a misconfiguration"); + } + + // "Standard" Pipeline + final IPortalRenderingPipeline standardRenderingPipeline = getStandardRenderingPipeline(); + + return new IPortalRenderingPipeline() { + @Override + public void renderState(HttpServletRequest req, HttpServletResponse res) + throws ServletException, IOException { + for (RenderingPipelineBranchPoint branchPoint : branches) { + if (branchPoint.renderStateIfApplicable(req, res)) { + /* + * Rendering bas been processed by the branch point -- no need to continue. + */ + return; + } + } + /* + * Reaching this point means that a branch was not followed; use the "standard" + * pipeline. + */ + standardRenderingPipeline.renderState(req, res); + } + }; + } + + /* + * Beans below this point are elements of the "standard" uPortal Rendering Pipeline -- where the + * uPortal webapp renders all elements of the UI and handles all requests. + */ + + @Bean(name = "standardRenderingPipeline") + public IPortalRenderingPipeline getStandardRenderingPipeline() { + final DynamicRenderingPipeline rslt = new DynamicRenderingPipeline(); + rslt.setPipeline(getAnalyticsIncorporationComponent()); + return rslt; + } + + @Bean(name = "userLayoutStoreComponent") + public StAXPipelineComponent getUserLayoutStoreComponent() { + return new UserLayoutStoreComponent(); + } + + @Bean(name = "postUserLayoutStoreLogger") + public StAXPipelineComponent getPostUserLayoutStoreLogger() { + final LoggingStAXComponent rslt = new LoggingStAXComponent(); + rslt.setWrappedComponent(getUserLayoutStoreComponent()); + rslt.setLoggerName(POST_USER_LAYOUT_STORE_LOGGER_NAME); + rslt.setLogEvents(false); + rslt.setLogFullDocument(true); + rslt.setStepIdentifier(POST_USER_LAYOUT_STORE_LOGGER_STEP_IDENTIFIER); + return rslt; + } + + @Bean(name = "themeAttributeSource") + public ThemeAttributeSource getThemeAttributeSource() { + return new ThemeAttributeSource(); + } + + @Bean(name = "dashboardWindowStateSettingsStAXComponent") + public StAXPipelineComponent getDashboardWindowStateSettingsStAXComponent() { + final WindowStateSettingsStAXComponent rslt = new WindowStateSettingsStAXComponent(); + rslt.setWrappedComponent(getPostUserLayoutStoreLogger()); + rslt.setStylesheetAttributeSource(getThemeAttributeSource()); + return rslt; + } + + @Bean(name = "attributeSource") + public PortletWindowAttributeSource getPortletWindowAttributeSource() { + return new PortletWindowAttributeSource(); + } + + @Bean(name = "portletWindowAttributeIncorporationComponent") + public StAXPipelineComponent getPortletWindowAttributeIncorporationComponent() { + final StAXAttributeIncorporationComponent rslt = new StAXAttributeIncorporationComponent(); + rslt.setWrappedComponent(getDashboardWindowStateSettingsStAXComponent()); + rslt.setAttributeSource(getPortletWindowAttributeSource()); + return rslt; + } + + @Bean(name = "structureAttributeSource") + public StructureAttributeSource getStructureAttributeSource() { + return new StructureAttributeSource(); + } + + @Bean(name = "structureAttributeIncorporationComponent") + public StAXPipelineComponent getStructureAttributeIncorporationComponent() { + final StAXAttributeIncorporationComponent rslt = new StAXAttributeIncorporationComponent(); + rslt.setWrappedComponent(getPortletWindowAttributeIncorporationComponent()); + rslt.setAttributeSource(getStructureAttributeSource()); + return rslt; + } + + @Bean(name = "preStructureTransformLogger") + public StAXPipelineComponent getPreStructureTransformLogger() { + final LoggingStAXComponent rslt = new LoggingStAXComponent(); + rslt.setWrappedComponent(getStructureAttributeIncorporationComponent()); + rslt.setLoggerName(PRE_STRUCTURE_TRANSFORM_LOGGER_NAME); + rslt.setLogEvents(false); + rslt.setLogFullDocument(true); + rslt.setStepIdentifier(PRE_STRUCTURE_TRANSFORM_LOGGER_STEP_IDENTIFIER); + return rslt; + } + + @Bean(name = "structureTransformSource") + public TransformerSource getStructureTransformSource() { + return new StructureTransformerSource(); + } + + @Bean + public TransformerConfigurationSource + getStructureStylesheetDescriptorTransformerConfigurationSource() { + return new StructureStylesheetDescriptorTransformerConfigurationSource(); + } + + @Bean + public TransformerConfigurationSource + getStructureStylesheetUserPreferencesTransformerConfigurationSource() { + return new StructureStylesheetUserPreferencesTransformerConfigurationSource(); + } + + @Bean + public TransformerConfigurationSource getStaticTransformerConfigurationSourceForStructure() { + final StaticTransformerConfigurationSource rslt = + new StaticTransformerConfigurationSource(); + rslt.setParameters(Collections.singletonMap("version-UP_FRAMEWORK", uPortalVersion)); + return rslt; + } + + @Bean + public TransformerConfigurationSource getUserImpersonationTransformerConfigurationSource() { + return new UserImpersonationTransformerConfigurationSource(); + } + + @Bean(name = "structureTransformComponent") + public StAXPipelineComponentWrapper getStructureTransformComponent() { + final XSLTComponent rslt = new XSLTComponent(); + rslt.setWrappedComponent(getPreStructureTransformLogger()); + rslt.setTransformerSource(getStructureTransformSource()); + final List sources = new ArrayList<>(); + sources.add(getStructureStylesheetDescriptorTransformerConfigurationSource()); + sources.add(getStructureStylesheetUserPreferencesTransformerConfigurationSource()); + sources.add(getStaticTransformerConfigurationSourceForStructure()); + sources.add(getUserImpersonationTransformerConfigurationSource()); + final MergingTransformerConfigurationSource mtcs = + new MergingTransformerConfigurationSource(); + mtcs.setSources(sources); + rslt.setXsltParameterSource(mtcs); + return rslt; + } + + @Bean(name = "postStructureTransformLogger") + public StAXPipelineComponent getPostStructureTransformLogger() { + final LoggingStAXComponent rslt = new LoggingStAXComponent(); + rslt.setWrappedComponent(getStructureTransformComponent()); + rslt.setLoggerName(POST_STRUCTURE_TRANSFORM_LOGGER_NAME); + rslt.setLogEvents(false); + rslt.setLogFullDocument(true); + rslt.setStepIdentifier(POST_STRUCTURE_TRANSFORM_LOGGER_STEP_IDENTIFIER); + return rslt; + } + + @Bean(name = "structureCachingComponent") + public StAXPipelineComponent getStructureCachingComponent() { + final CachingStAXPipelineComponent rslt = new CachingStAXPipelineComponent(); + rslt.setWrappedComponent(getPostStructureTransformLogger()); + rslt.setCache(structureTransformCache); + return rslt; + } + + @Bean(name = "portletRenderingInitiationComponent") + public StAXPipelineComponentWrapper getPortletRenderingInitiationComponent() { + final PortletRenderingInitiationStAXComponent rslt = + new PortletRenderingInitiationStAXComponent(); + rslt.setWrappedComponent(getStructureCachingComponent()); + return rslt; + } + + @Bean(name = "themeAttributeIncorporationComponent") + public StAXPipelineComponent getThemeAttributeIncorporationComponent() { + final StAXAttributeIncorporationComponent rslt = new StAXAttributeIncorporationComponent(); + rslt.setWrappedComponent(getPortletRenderingInitiationComponent()); + rslt.setAttributeSource(getThemeAttributeSource()); + return rslt; + } + + @Bean(name = "preThemeTransformLogger") + public StAXPipelineComponent getPreThemeTransformLogger() { + final LoggingStAXComponent rslt = new LoggingStAXComponent(); + rslt.setWrappedComponent(getThemeAttributeIncorporationComponent()); + rslt.setLoggerName(PRE_THEME_TRANSFORM_LOGGER_NAME); + rslt.setLogEvents(false); + rslt.setLogFullDocument(true); + rslt.setStepIdentifier(PRE_THEME_TRANSFORM_LOGGER_STEP_IDENTIFIER); + return rslt; + } + + @Bean(name = "themeTransformSource") + public TransformerSource getThemeTransformerSource() { + return new ThemeTransformerSource(); + } + + @Bean + public TransformerConfigurationSource + getThemeStylesheetDescriptorTransformerConfigurationSource() { + return new ThemeStylesheetDescriptorTransformerConfigurationSource(); + } + + @Bean + public TransformerConfigurationSource + getThemeStylesheetUserPreferencesTransformerConfigurationSource() { + return new ThemeStylesheetUserPreferencesTransformerConfigurationSource(); + } + + @Bean + public TransformerConfigurationSource getStaticTransformerConfigurationSourceForTheme() { + + final Map parameters = new HashMap<>(); + parameters.put(XsltPortalUrlProvider.XSLT_PORTAL_URL_PROVIDER, xslPortalUrlProvider); + parameters.put("EXTERNAL_LOGIN_URL", casLoginUrl); + parameters.put("useTabGroups", useTabGroups); + parameters.put("UP_VERSION", uPortalVersion); + parameters.put("USE_FLYOUT_MENUS", useFlyoutMenus); + + final Map parameterExpressions = new HashMap<>(); + parameterExpressions.put("CURRENT_REQUEST", "request.nativeRequest"); + parameterExpressions.put("CONTEXT_PATH", "request.contextPath"); + parameterExpressions.put("HOST_NAME", "request.nativeRequest.serverName"); + parameterExpressions.put("AUTHENTICATED", "!person.guest"); + parameterExpressions.put("userName", "person.fullName"); + parameterExpressions.put("USER_ID", "person.userName"); + parameterExpressions.put("SERVER_NAME", "@portalInfoProvider.serverName"); + parameterExpressions.put( + "STATS_SESSION_ID", + "@portalEventFactory.getPortalEventSessionId(request.nativeRequest, person)"); + + final Set cacheKeyExcludedParameters = new HashSet<>(); + cacheKeyExcludedParameters.add("CURRENT_REQUEST"); + cacheKeyExcludedParameters.add( + org.apereo.portal.web.skin.ResourcesElementsXsltcHelper.RESOURCES_ELEMENTS_HELPER); + cacheKeyExcludedParameters.add( + org.apereo.portal.url.xml.XsltPortalUrlProvider.XSLT_PORTAL_URL_PROVIDER); + + final StaticTransformerConfigurationSource rslt = + new StaticTransformerConfigurationSource(); + rslt.setParameters(parameters); + rslt.setParameterExpressions(parameterExpressions); + rslt.setCacheKeyExcludedParameters(cacheKeyExcludedParameters); + return rslt; + } + + @Bean + public TransformerConfigurationSource getLocaleTransformerConfigurationSource() { + return new LocaleTransformerConfigurationSource(); + } + + @Bean + public TransformerConfigurationSource getResourcesElementsXsltcHelper() { + return new ResourcesElementsXsltcHelper(); + } + + @Bean(name = "themeTransformComponent") + public StAXPipelineComponentWrapper getThemeTransformComponent() { + final XSLTComponent rslt = new XSLTComponent(); + rslt.setWrappedComponent(getPreThemeTransformLogger()); + rslt.setTransformerSource(getThemeTransformerSource()); + final List sources = new ArrayList<>(); + sources.add(getThemeStylesheetDescriptorTransformerConfigurationSource()); + sources.add(getThemeStylesheetUserPreferencesTransformerConfigurationSource()); + sources.add(getStaticTransformerConfigurationSourceForTheme()); + sources.add(getLocaleTransformerConfigurationSource()); + sources.add(getResourcesElementsXsltcHelper()); + final MergingTransformerConfigurationSource mtcs = + new MergingTransformerConfigurationSource(); + mtcs.setSources(sources); + rslt.setXsltParameterSource(mtcs); + return rslt; + } + + @Bean(name = "postThemeTransformLogger") + public StAXPipelineComponent getPostThemeTransformLogger() { + final LoggingStAXComponent rslt = new LoggingStAXComponent(); + rslt.setWrappedComponent(getThemeTransformComponent()); + rslt.setLoggerName(POST_THEME_TRANSFORM_LOGGER_NAME); + rslt.setLogEvents(false); + rslt.setLogFullDocument(true); + rslt.setLogFullDocumentAsHtml(true); + rslt.setStepIdentifier(POST_THEME_TRANSFORM_LOGGER_STEP_IDENTIFIER); + return rslt; + } + + @Bean + public CharacterEventSource getPortletContentPlaceholderEventSource() { + return new PortletContentPlaceholderEventSource(); + } + + @Bean + public CharacterEventSource getPortletHeaderPlaceholderEventSource() { + return new PortletHeaderPlaceholderEventSource(); + } + + @Bean + public CharacterEventSource getChunkPointPlaceholderEventSource() { + return new ChunkPointPlaceholderEventSource(); + } + + @Bean + public CharacterEventSource getPortletAnalyticsDataPlaceholderEventSource() { + return new PortletAnalyticsDataPlaceholderEventSource(); + } + + @Bean + public CharacterEventSource getPageAnalyticsDataPlaceholderEventSource() { + return new PageAnalyticsDataPlaceholderEventSource(); + } + + @Bean + public CharacterEventSource getPortletTitlePlaceholderEventSource() { + return new PortletTitlePlaceholderEventSource(); + } + + @Bean + public CharacterEventSource getPortletHelpPlaceholderEventSource() { + return new PortletHelpPlaceholderEventSource(); + } + + @Bean + public CharacterEventSource getPortletNewItemCountPlaceholderEventSource() { + return new PortletNewItemCountPlaceholderEventSource(); + } + + @Bean + public CharacterEventSource getPortletLinkPlaceholderEventSource() { + return new PortletLinkPlaceholderEventSource(); + } + + @Bean(name = "staxSerializingComponent") + public CharacterPipelineComponent getStAXSerializingComponent() { + final StAXSerializingComponent rslt = new StAXSerializingComponent(); + rslt.setWrappedComponent(getPostThemeTransformLogger()); + + final Map chunkingElements = new HashMap<>(); + chunkingElements.put(IUserLayoutManager.CHANNEL, getPortletContentPlaceholderEventSource()); + chunkingElements.put( + IUserLayoutManager.CHANNEL_HEADER, getPortletHeaderPlaceholderEventSource()); + chunkingElements.put( + ChunkPointPlaceholderEventSource.CHUNK_POINT, + getChunkPointPlaceholderEventSource()); + chunkingElements.put( + PortletAnalyticsDataPlaceholderEventSource.PORTLET_ANALYTICS_SCRIPT, + getPortletAnalyticsDataPlaceholderEventSource()); + chunkingElements.put( + PageAnalyticsDataPlaceholderEventSource.PAGE_ANALYTICS_SCRIPT, + getPageAnalyticsDataPlaceholderEventSource()); + rslt.setChunkingElements(chunkingElements); + + final Map chunkingPatterns = new HashMap<>(); + chunkingPatterns.put(PORTLET_TITLE_PATTERN, getPortletTitlePlaceholderEventSource()); + chunkingPatterns.put(PORTLET_HELP_PATTERN, getPortletHelpPlaceholderEventSource()); + chunkingPatterns.put( + PORTLET_NEW_ITEM_COUNT_PATTERN, getPortletNewItemCountPlaceholderEventSource()); + chunkingPatterns.put(PORTLET_LINK_PATTERN, getPortletLinkPlaceholderEventSource()); + rslt.setChunkingPatterns(chunkingPatterns); + + return rslt; + } + + @Bean(name = "postSerializerLogger") + public CharacterPipelineComponent getPostSerializerLogger() { + final LoggingCharacterComponent rslt = new LoggingCharacterComponent(); + rslt.setWrappedComponent(getStAXSerializingComponent()); + rslt.setLoggerName("org.apereo.portal.rendering.LoggingCharacterComponent.POST_SERIALIZER"); + return rslt; + } + + @Bean(name = "themeCachingComponent") + public CharacterPipelineComponent getThemeCachingComponent() { + final CachingCharacterPipelineComponent rslt = new CachingCharacterPipelineComponent(); + rslt.setWrappedComponent(getPostSerializerLogger()); + rslt.setCache(themeTransformCache); + return rslt; + } + + @Bean(name = "portletRenderingInitiationCharacterComponent") + public CharacterPipelineComponent getPortletRenderingInitiationCharacterComponent() { + final PortletRenderingInitiationCharacterComponent rslt = + new PortletRenderingInitiationCharacterComponent(); + rslt.setWrappedComponent(getThemeCachingComponent()); + return rslt; + } + + @Bean(name = "portletRenderingIncorporationComponent") + public CharacterPipelineComponent getPortletRenderingIncorporationComponent() { + final PortletRenderingIncorporationComponent rslt = + new PortletRenderingIncorporationComponent(); + rslt.setWrappedComponent(getPortletRenderingInitiationCharacterComponent()); + return rslt; + } + + @Bean(name = "analyticsIncorporationComponent") + public CharacterPipelineComponent getAnalyticsIncorporationComponent() { + final AnalyticsIncorporationComponent rslt = new AnalyticsIncorporationComponent(); + rslt.setWrappedComponent(getPortletRenderingIncorporationComponent()); + return rslt; + } + + /** + * This bean is not an element of the rendering pipeline. It is a DAO for reading and writing + * Resources objects to files. + */ + @Bean(name = "resourcesDao") + public ResourcesDao getResourcesDao() { + return new ResourcesDaoImpl(); + } + + /** This bean is not an element of the rendering pipeline. */ + @Bean(name = "resourcesElementsProvider") + public ResourcesElementsProvider getResourcesElementsProvider() { + ResourcesElementsProviderImpl rslt = new ResourcesElementsProviderImpl(); + rslt.setResourcesDao(getResourcesDao()); + return rslt; + } +} diff --git a/uPortal-webapp/src/main/resources/properties/contexts/portletContainerContext.xml b/uPortal-webapp/src/main/resources/properties/contexts/portletContainerContext.xml index 61723fb9ce1..bd767ec5328 100644 --- a/uPortal-webapp/src/main/resources/properties/contexts/portletContainerContext.xml +++ b/uPortal-webapp/src/main/resources/properties/contexts/portletContainerContext.xml @@ -25,7 +25,7 @@ xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd"> - + - - + - + @@ -86,7 +86,7 @@ - + @@ -113,7 +113,7 @@ - + @@ -144,7 +144,7 @@ class="org.apereo.portal.portlet.container.services.CachedPasswordUserInfoService"> - + @@ -165,17 +165,17 @@ - + - + - - + diff --git a/uPortal-webapp/src/main/resources/properties/contexts/renderingPipelineContext.xml b/uPortal-webapp/src/main/resources/properties/contexts/renderingPipelineContext.xml deleted file mode 100644 index 00f094d092c..00000000000 --- a/uPortal-webapp/src/main/resources/properties/contexts/renderingPipelineContext.xml +++ /dev/null @@ -1,302 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CURRENT_REQUEST - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -