/** * 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: * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>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.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.namespace.QName; import javax.xml.stream.XMLEventFactory; import javax.xml.stream.XMLEventReader; import javax.xml.stream.events.Attribute; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; import org.apereo.portal.utils.cache.CacheKey; import org.apereo.portal.xml.XmlUtilitiesImpl; import org.apereo.portal.xml.stream.FilteringXMLEventReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.BeanNameAware; /** */ public class StAXAttributeIncorporationComponent extends StAXPipelineComponentWrapper implements BeanNameAware { protected final Logger logger = LoggerFactory.getLogger(getClass()); private final XMLEventFactory eventFactory = XMLEventFactory.newFactory(); private AttributeSource attributeSource; private String beanName; public void setAttributeSource(AttributeSource attributeSource) { this.attributeSource = attributeSource; } @Override public void setBeanName(String name) { this.beanName = name; } /* (non-Javadoc) * @see org.apereo.portal.rendering.PipelineComponent#getCacheKey(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override public CacheKey getCacheKey(HttpServletRequest request, HttpServletResponse response) { final CacheKey parentKey = this.wrappedComponent.getCacheKey(request, response); final CacheKey attributeKey = this.attributeSource.getCacheKey(request, response); return CacheKey.build(this.beanName, parentKey, attributeKey); } /* (non-Javadoc) * @see org.apereo.portal.rendering.PipelineComponent#getEventReader(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override public PipelineEventReader<XMLEventReader, XMLEvent> getEventReader( HttpServletRequest request, HttpServletResponse response) { //Get the reader from the parent and add the attribute incorporating wrapper final PipelineEventReader<XMLEventReader, XMLEvent> cachingEventReader = this.wrappedComponent.getEventReader(request, response); final XMLEventReader eventReader = new AttributeIncorporatingXMLEventReader( request, response, cachingEventReader.getEventReader()); final Map<String, String> outputProperties = cachingEventReader.getOutputProperties(); return new PipelineEventReaderImpl<XMLEventReader, XMLEvent>(eventReader, outputProperties); } private final class AttributeIncorporatingXMLEventReader extends FilteringXMLEventReader { private final HttpServletRequest request; private final HttpServletResponse response; public AttributeIncorporatingXMLEventReader( HttpServletRequest request, HttpServletResponse response, XMLEventReader reader) { super(reader); this.request = request; this.response = response; } @Override protected XMLEvent filterEvent(XMLEvent event, boolean peek) { //Only filtering start elements to add attributes if (!event.isStartElement()) { return event; } //Get the additional attributes, return if there are no attributes for this element final StartElement startElement = event.asStartElement(); final Iterator<Attribute> additionalAttributes = attributeSource.getAdditionalAttributes(request, response, startElement); if (additionalAttributes == null || !additionalAttributes.hasNext()) { return event; } //Merge the additional attributes with the existing attributes //additional attributes overwrite final Map<QName, Attribute> mergedAttributes = new LinkedHashMap<QName, Attribute>(); for (final Iterator<Attribute> attributes = startElement.getAttributes(); attributes.hasNext(); ) { final Attribute attribute = attributes.next(); mergedAttributes.put(attribute.getName(), attribute); } while (additionalAttributes.hasNext()) { final Attribute attribute = additionalAttributes.next(); mergedAttributes.put(attribute.getName(), attribute); if (logger.isDebugEnabled()) { logger.debug( "{} - Adding Attribute {} to event {}", new Object[] { beanName, XmlUtilitiesImpl.toString(attribute), XmlUtilitiesImpl.toString(event) }); } } //Create the modified StartElement with the additional attribute data final StartElement modifiedStartElement = eventFactory.createStartElement( startElement.getName(), mergedAttributes.values().iterator(), startElement.getNamespaces()); return modifiedStartElement; } } }