blob: fb83ebe6d85d44c8526f792993bc54d87945e899 [file] [log] [blame]
/**
* 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.xml.security.stax.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.stax.ext.*;
import org.apache.xml.security.stax.ext.stax.XMLSecEvent;
import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import java.util.ArrayList;
import java.util.List;
/**
* Implementation of a OutputProcessorChain
*
*/
public class OutputProcessorChainImpl implements OutputProcessorChain {
protected static final transient Logger LOG = LoggerFactory.getLogger(OutputProcessorChainImpl.class);
private List<OutputProcessor> outputProcessors;
private int startPos;
private int curPos;
private XMLSecStartElement parentXmlSecStartElement;
private final OutboundSecurityContext outboundSecurityContext;
private final DocumentContextImpl documentContext;
public OutputProcessorChainImpl(OutboundSecurityContext outboundSecurityContext) {
this(outboundSecurityContext, 0);
}
public OutputProcessorChainImpl(OutboundSecurityContext outboundSecurityContext, int startPos) {
this(outboundSecurityContext, new DocumentContextImpl(), startPos, new ArrayList<>(20));
}
public OutputProcessorChainImpl(OutboundSecurityContext outboundSecurityContext, DocumentContextImpl documentContext) {
this(outboundSecurityContext, documentContext, 0, new ArrayList<>(20));
}
protected OutputProcessorChainImpl(OutboundSecurityContext outboundSecurityContext, DocumentContextImpl documentContextImpl,
int startPos, List<OutputProcessor> outputProcessors) {
this.outboundSecurityContext = outboundSecurityContext;
this.curPos = this.startPos = startPos;
documentContext = documentContextImpl;
this.outputProcessors = outputProcessors;
}
@Override
public void reset() {
this.curPos = startPos;
}
@Override
public OutboundSecurityContext getSecurityContext() {
return this.outboundSecurityContext;
}
@Override
public DocumentContext getDocumentContext() {
return this.documentContext;
}
@Override
public void addProcessor(OutputProcessor newOutputProcessor) {
int startPhaseIdx = 0;
int endPhaseIdx = outputProcessors.size();
int idxToInsert = endPhaseIdx;
XMLSecurityConstants.Phase targetPhase = newOutputProcessor.getPhase();
for (int i = outputProcessors.size() - 1; i >= 0; i--) {
OutputProcessor outputProcessor = outputProcessors.get(i);
if (outputProcessor.getPhase().ordinal() < targetPhase.ordinal()) {
startPhaseIdx = i + 1;
break;
}
}
for (int i = startPhaseIdx; i < outputProcessors.size(); i++) {
OutputProcessor outputProcessor = outputProcessors.get(i);
if (outputProcessor.getPhase().ordinal() > targetPhase.ordinal()) {
endPhaseIdx = i;
break;
}
}
//just look for the correct phase and append as last
if (newOutputProcessor.getBeforeProcessors().isEmpty()
&& newOutputProcessor.getAfterProcessors().isEmpty()) {
outputProcessors.add(endPhaseIdx, newOutputProcessor);
} else if (newOutputProcessor.getBeforeProcessors().isEmpty()) {
idxToInsert = endPhaseIdx;
for (int i = endPhaseIdx - 1; i >= startPhaseIdx; i--) {
OutputProcessor outputProcessor = outputProcessors.get(i);
if (newOutputProcessor.getAfterProcessors().contains(outputProcessor)
|| newOutputProcessor.getAfterProcessors().contains(outputProcessor.getClass().getName())) {
idxToInsert = i + 1;
break;
}
}
outputProcessors.add(idxToInsert, newOutputProcessor);
} else if (newOutputProcessor.getAfterProcessors().isEmpty()) {
idxToInsert = startPhaseIdx;
for (int i = startPhaseIdx; i < endPhaseIdx; i++) {
OutputProcessor outputProcessor = outputProcessors.get(i);
if (newOutputProcessor.getBeforeProcessors().contains(outputProcessor)
|| newOutputProcessor.getBeforeProcessors().contains(outputProcessor.getClass().getName())) {
idxToInsert = i;
break;
}
}
outputProcessors.add(idxToInsert, newOutputProcessor);
} else {
boolean found = false;
idxToInsert = endPhaseIdx;
for (int i = startPhaseIdx; i < endPhaseIdx; i++) {
OutputProcessor outputProcessor = outputProcessors.get(i);
if (newOutputProcessor.getBeforeProcessors().contains(outputProcessor)
|| newOutputProcessor.getBeforeProcessors().contains(outputProcessor.getClass().getName())) {
idxToInsert = i;
found = true;
break;
}
}
if (found) {
outputProcessors.add(idxToInsert, newOutputProcessor);
} else {
for (int i = endPhaseIdx - 1; i >= startPhaseIdx; i--) {
OutputProcessor outputProcessor = outputProcessors.get(i);
if (newOutputProcessor.getAfterProcessors().contains(outputProcessor)
|| newOutputProcessor.getAfterProcessors().contains(outputProcessor.getClass().getName())) {
idxToInsert = i + 1;
break;
}
}
outputProcessors.add(idxToInsert, newOutputProcessor);
}
}
if (idxToInsert < this.curPos) {
this.curPos++;
}
if (LOG.isDebugEnabled()) {
LOG.debug("Added {} to output chain: ", newOutputProcessor.getClass().getName());
for (int i = 0; i < outputProcessors.size(); i++) {
OutputProcessor outputProcessor = outputProcessors.get(i);
LOG.debug("Name: {} phase: {}", outputProcessor.getClass().getName(), outputProcessor.getPhase());
}
}
}
@Override
public void removeProcessor(OutputProcessor outputProcessor) {
LOG.debug("Removing processor {} from output chain", outputProcessor.getClass().getName());
if (this.outputProcessors.indexOf(outputProcessor) <= this.curPos) {
this.curPos--;
}
this.outputProcessors.remove(outputProcessor);
}
@Override
public List<OutputProcessor> getProcessors() {
return this.outputProcessors;
}
private void setParentXmlSecStartElement(XMLSecStartElement xmlSecStartElement) {
this.parentXmlSecStartElement = xmlSecStartElement;
}
@Override
public void processEvent(XMLSecEvent xmlSecEvent) throws XMLStreamException, XMLSecurityException {
boolean reparent = false;
if (this.curPos == this.startPos) {
switch (xmlSecEvent.getEventType()) {
case XMLStreamConstants.START_ELEMENT:
if (xmlSecEvent == parentXmlSecStartElement) {
parentXmlSecStartElement = null;
}
xmlSecEvent.setParentXMLSecStartElement(parentXmlSecStartElement);
parentXmlSecStartElement = xmlSecEvent.asStartElement();
break;
case XMLStreamConstants.END_ELEMENT:
xmlSecEvent.setParentXMLSecStartElement(parentXmlSecStartElement);
reparent = true;
break;
default:
xmlSecEvent.setParentXMLSecStartElement(parentXmlSecStartElement);
break;
}
}
outputProcessors.get(this.curPos++).processNextEvent(xmlSecEvent, this);
if (reparent && parentXmlSecStartElement != null) {
parentXmlSecStartElement = parentXmlSecStartElement.getParentXMLSecStartElement();
}
}
@Override
public void doFinal() throws XMLStreamException, XMLSecurityException {
outputProcessors.get(this.curPos++).doFinal(this);
}
@Override
public OutputProcessorChain createSubChain(OutputProcessor outputProcessor) throws XMLStreamException, XMLSecurityException {
return createSubChain(outputProcessor, null);
}
@Override
public OutputProcessorChain createSubChain(OutputProcessor outputProcessor, XMLSecStartElement parentXMLSecStartElement) throws XMLStreamException, XMLSecurityException {
//we don't clone the processor-list to get updates in the sublist too!
OutputProcessorChainImpl outputProcessorChain;
try {
outputProcessorChain = new OutputProcessorChainImpl(outboundSecurityContext, documentContext.clone(),
outputProcessors.indexOf(outputProcessor) + 1, this.outputProcessors);
} catch (CloneNotSupportedException e) {
throw new XMLSecurityException(e);
}
if (parentXMLSecStartElement != null) {
outputProcessorChain.setParentXmlSecStartElement(parentXMLSecStartElement);
} else {
outputProcessorChain.setParentXmlSecStartElement(this.parentXmlSecStartElement);
}
return outputProcessorChain;
}
}