/** * Copyright 2015 StreamSets Inc. * * Licensed under 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 com.streamsets.pipeline.lib.xml; import com.streamsets.pipeline.api.Field; import com.streamsets.pipeline.api.ext.io.OverrunReader; import com.streamsets.pipeline.api.impl.Utils; import com.streamsets.pipeline.api.ext.io.ObjectLengthException; import com.streamsets.pipeline.api.ext.io.OverrunException; import com.streamsets.pipeline.api.ext.io.OverrunReader; import javax.xml.stream.XMLStreamException; import java.io.IOException; import java.io.Reader; import java.util.Map; public class OverrunStreamingXmlParser extends StreamingXmlParser { private final OverrunReader countingReader; private final int maxObjectLen; private long limit; private boolean overrun; private long initialPosition; public OverrunStreamingXmlParser(Reader reader, String recordElement, long initialPosition, int maxObjectLen) throws IOException, XMLStreamException { this( new OverrunReader( reader, OverrunReader.getDefaultReadLimit(), false, false ), recordElement, null, initialPosition, maxObjectLen, true ); this.initialPosition = initialPosition; } public OverrunStreamingXmlParser(OverrunReader reader, String recordElement, Map<String, String> namespaces, long initialPosition, int maxObjectLen, boolean useFieldAttributesInsteadOfFields) throws IOException, XMLStreamException { super(reader, recordElement, namespaces, initialPosition, useFieldAttributesInsteadOfFields); countingReader = (OverrunReader) getReader(); countingReader.setEnabled(true); this.maxObjectLen = maxObjectLen; this.initialPosition = initialPosition; } @Override protected void fastForwardLeaseReader() { ((OverrunReader) getReader()).resetCount(); } @Override protected boolean isOverMaxObjectLength() throws XMLStreamException { return (maxObjectLen > -1) && getReaderPosition() > limit; } @Override public Field read() throws IOException, XMLStreamException { Field field; Utils.checkState(!overrun, "The underlying input stream had an overrun, the parser is not usable anymore"); countingReader.resetCount(); limit = getReaderPosition() + maxObjectLen; try { field = super.read(); throwIfOverMaxObjectLength(); initialPosition = getReaderPosition(); } catch (XMLStreamException ex) { if (ex.getNestedException() != null && ex.getNestedException() instanceof OverrunException) { overrun = true; throw (OverrunException) ex.getNestedException(); } throw ex; } return field; } @Override protected void throwIfOverMaxObjectLength() throws XMLStreamException, ObjectLengthException { if (isOverMaxObjectLength()) { throw new ObjectLengthException( Utils.format("XML Object at offset '{}' exceeds max length '{}'; current position '{}'", initialPosition, maxObjectLen, getReaderPosition() ), getReaderPosition() ); } } }