org.apache.hadoop.gateway.filter.rewrite.impl.json.JsonFilterReader.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.gateway.filter.rewrite.impl.json.JsonFilterReader.java

Source

/**
 * 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.hadoop.gateway.filter.rewrite.impl.json;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteFilterApplyDescriptor;
import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteFilterBufferDescriptor;
import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteFilterDetectDescriptor;
import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteFilterGroupDescriptor;
import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteFilterPathDescriptor;
import org.apache.hadoop.gateway.filter.rewrite.i18n.UrlRewriteMessages;
import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
import org.apache.hadoop.gateway.util.JsonPath;

import java.io.IOException;
import java.io.Reader;
import java.io.StringWriter;
import java.util.List;
import java.util.Stack;
import java.util.regex.Pattern;

class JsonFilterReader extends Reader {

    private static final UrlRewriteMessages LOG = MessagesFactory.get(UrlRewriteMessages.class);

    private static final UrlRewriteFilterPathDescriptor.Compiler<JsonPath.Expression> JPATH_COMPILER = new JsonPathCompiler();
    private static final UrlRewriteFilterPathDescriptor.Compiler<Pattern> REGEX_COMPILER = new RegexCompiler();

    private JsonFactory factory;
    private JsonParser parser;
    private JsonGenerator generator;
    private ObjectMapper mapper;

    private Reader reader;
    private int offset;
    private StringWriter writer;
    private StringBuffer buffer;
    private Stack<Level> stack;
    private Level bufferingLevel;
    private UrlRewriteFilterBufferDescriptor bufferingConfig;
    private UrlRewriteFilterGroupDescriptor config;

    public JsonFilterReader(Reader reader, UrlRewriteFilterContentDescriptor config) throws IOException {
        this.reader = reader;
        factory = new JsonFactory();
        mapper = new ObjectMapper();
        parser = factory.createParser(reader);
        writer = new StringWriter();
        buffer = writer.getBuffer();
        offset = 0;
        generator = factory.createGenerator(writer);
        stack = new Stack<Level>();
        bufferingLevel = null;
        bufferingConfig = null;
        this.config = config;
    }

    @Override
    public int read(char[] destBuffer, int destOffset, int destCount) throws IOException {
        int count = 0;
        int available = buffer.length() - offset;

        if (available == 0) {
            JsonToken token = parser.nextToken();
            if (token == null) {
                count = -1;
            } else {
                processCurrentToken();
                available = buffer.length() - offset;
            }
        }

        if (available > 0) {
            count = Math.min(destCount, available);
            buffer.getChars(offset, offset + count, destBuffer, destOffset);
            offset += count;
            if (offset == buffer.length()) {
                offset = 0;
                buffer.setLength(0);
            }
        }

        return count;
    }

    private void processCurrentToken() throws IOException {
        switch (parser.getCurrentToken()) {
        case START_OBJECT:
            processStartObject();
            break;
        case END_OBJECT:
            processEndObject();
            break;
        case START_ARRAY:
            processStartArray();
            break;
        case END_ARRAY:
            processEndArray();
            break;
        case FIELD_NAME:
            processFieldName(); // Could be the name of an object, array or value.
            break;
        case VALUE_STRING:
            processValueString();
            break;
        case VALUE_NUMBER_INT:
        case VALUE_NUMBER_FLOAT:
            processValueNumber();
            break;
        case VALUE_TRUE:
        case VALUE_FALSE:
            processValueBoolean();
            break;
        case VALUE_NULL:
            processValueNull();
            break;
        case NOT_AVAILABLE:
            // Ignore it.
            break;
        }
        generator.flush();
    }

    private Level pushLevel(String field, JsonNode node, JsonNode scopeNode,
            UrlRewriteFilterGroupDescriptor scopeConfig) {
        if (!stack.isEmpty()) {
            Level top = stack.peek();
            if (scopeNode == null) {
                scopeNode = top.scopeNode;
                scopeConfig = top.scopeConfig;
            }
        }
        Level level = new Level(field, node, scopeNode, scopeConfig);
        stack.push(level);
        return level;
    }

    private void processStartObject() throws IOException {
        JsonNode node;
        Level child;
        Level parent;
        if (stack.isEmpty()) {
            node = mapper.createObjectNode();
            child = pushLevel(null, node, node, config);
        } else {
            child = stack.peek();
            if (child.node == null) {
                child.node = mapper.createObjectNode();
                parent = stack.get(stack.size() - 2);
                switch (parent.node.asToken()) {
                case START_ARRAY:
                    ((ArrayNode) parent.node).add(child.node);
                    break;
                case START_OBJECT:
                    ((ObjectNode) parent.node).put(child.field, child.node);
                    break;
                default:
                    throw new IllegalStateException();
                }
            } else if (child.isArray()) {
                parent = child;
                node = mapper.createObjectNode();
                child = pushLevel(null, node, null, null);
                ((ArrayNode) parent.node).add(child.node);
            } else {
                throw new IllegalStateException();
            }
        }
        if (bufferingLevel == null) {
            if (!startBuffering(child)) {
                generator.writeStartObject();
            }
        }
    }

    private void processEndObject() throws IOException {
        Level child;
        Level parent;
        child = stack.pop();
        if (bufferingLevel == child) {
            filterBufferedNode(child);
            mapper.writeTree(generator, child.node);
            bufferingLevel = null;
            bufferingConfig = null;
        } else if (bufferingLevel == null) {
            generator.writeEndObject();
            if (!stack.isEmpty()) {
                parent = stack.peek();
                switch (parent.node.asToken()) {
                case START_ARRAY:
                    ((ArrayNode) parent.node).removeAll();
                    break;
                case START_OBJECT:
                    ((ObjectNode) parent.node).removeAll();
                    break;
                default:
                    throw new IllegalStateException();
                }
            }
        }
    }

    private void processStartArray() throws IOException {
        JsonNode node;
        Level child;
        Level parent;
        if (stack.isEmpty()) {
            node = mapper.createArrayNode();
            child = pushLevel(null, node, node, config);
        } else {
            child = stack.peek();
            if (child.node == null) {
                child.node = mapper.createArrayNode();
                parent = stack.get(stack.size() - 2);
                switch (parent.node.asToken()) {
                case START_ARRAY:
                    ((ArrayNode) parent.node).add(child.node);
                    break;
                case START_OBJECT:
                    ((ObjectNode) parent.node).put(child.field, child.node);
                    break;
                default:
                    throw new IllegalStateException();
                }
            } else if (child.isArray()) {
                parent = child;
                child = pushLevel(null, mapper.createArrayNode(), null, null);
                ((ArrayNode) parent.node).add(child.node);
            } else {
                throw new IllegalStateException();
            }
        }
        if (bufferingLevel == null) {
            if (!startBuffering(child)) {
                generator.writeStartArray();
            }
        }
    }

    private void processEndArray() throws IOException {
        Level child;
        Level parent;
        child = stack.pop();
        if (bufferingLevel == child) {
            filterBufferedNode(child);
            mapper.writeTree(generator, child.node);
            bufferingLevel = null;
            bufferingConfig = null;
        } else if (bufferingLevel == null) {
            generator.writeEndArray();
            if (!stack.isEmpty()) {
                parent = stack.peek();
                switch (parent.node.asToken()) {
                case START_ARRAY:
                    ((ArrayNode) parent.node).removeAll();
                    break;
                case START_OBJECT:
                    ((ObjectNode) parent.node).removeAll();
                    break;
                default:
                    throw new IllegalStateException();
                }
            }
        }
    }

    private void processFieldName() throws IOException {
        Level child = pushLevel(parser.getCurrentName(), null, null, null);
        try {
            child.field = filterFieldName(child.field);
        } catch (Exception e) {
            LOG.failedToFilterFieldName(child.field, e);
            // Write original name.
        }
        if (bufferingLevel == null) {
            generator.writeFieldName(child.field);
        }
    }

    private void processValueString() throws IOException {
        Level child;
        Level parent;
        String value = null;
        parent = stack.peek();
        if (parent.isArray()) {
            ArrayNode array = (ArrayNode) parent.node;
            array.add(parser.getText());
            if (bufferingLevel == null) {
                value = filterStreamValue(parent);
                array.set(array.size() - 1, new TextNode(value));
            } else {
                array.removeAll();
            }
        } else {
            child = stack.pop();
            parent = stack.peek();
            ((ObjectNode) parent.node).put(child.field, parser.getText());
            if (bufferingLevel == null) {
                child.node = parent.node; // Populate the JsonNode of the child for filtering.
                value = filterStreamValue(child);
            }
        }
        if (bufferingLevel == null) {
            if (parent.node.isArray()) {
                ((ArrayNode) parent.node).removeAll();
            } else {
                ((ObjectNode) parent.node).removeAll();
            }
            generator.writeString(value);
        }
    }

    private void processValueNumber() throws IOException {
        Level child;
        Level parent;
        parent = stack.peek();
        if (parent.isArray()) {
            if (bufferingLevel != null) {
                ArrayNode array = (ArrayNode) parent.node;
                array.add(parser.getDecimalValue());
            }
        } else {
            child = stack.pop();
            if (bufferingLevel != null) {
                parent = stack.peek();
                ObjectNode object = (ObjectNode) parent.node;
                object.put(child.field, parser.getDecimalValue());
            }
        }
        if (bufferingLevel == null) {
            switch (parser.getNumberType()) {
            case INT:
                generator.writeNumber(parser.getIntValue());
                break;
            case LONG:
                generator.writeNumber(parser.getLongValue());
                break;
            case BIG_INTEGER:
                generator.writeNumber(parser.getBigIntegerValue());
                break;
            case FLOAT:
                generator.writeNumber(parser.getFloatValue());
                break;
            case DOUBLE:
                generator.writeNumber(parser.getDoubleValue());
                break;
            case BIG_DECIMAL:
                generator.writeNumber(parser.getDecimalValue());
                break;
            }
        }
    }

    private void processValueBoolean() throws IOException {
        Level child;
        Level parent;
        parent = stack.peek();
        if (parent.isArray()) {
            ((ArrayNode) parent.node).add(parser.getBooleanValue());
            //dump();
            if (bufferingLevel == null) {
                ((ArrayNode) parent.node).removeAll();
            }
        } else {
            child = stack.pop();
            parent = stack.peek();
            ((ObjectNode) parent.node).put(child.field, parser.getBooleanValue());
            //dump();
            if (bufferingLevel == null) {
                ((ObjectNode) parent.node).remove(child.field);
            }
        }
        if (bufferingLevel == null) {
            generator.writeBoolean(parser.getBooleanValue());
        }
    }

    private void processValueNull() throws IOException {
        Level child;
        Level parent = stack.peek();
        if (parent.isArray()) {
            ((ArrayNode) parent.node).addNull();
            //dump();
            if (bufferingLevel == null) {
                ((ArrayNode) parent.node).removeAll();
            }
        } else {
            child = stack.pop();
            parent = stack.peek();
            ((ObjectNode) parent.node).putNull(child.field);
            //dump();
            if (bufferingLevel == null) {
                ((ObjectNode) parent.node).remove(child.field);
            }
        }
        if (bufferingLevel == null) {
            generator.writeNull();
        }
    }

    protected boolean startBuffering(Level node) {
        boolean buffered = false;
        UrlRewriteFilterGroupDescriptor scope = node.scopeConfig;
        if (scope != null) {
            for (UrlRewriteFilterPathDescriptor selector : scope.getSelectors()) {
                JsonPath.Expression path = (JsonPath.Expression) selector.compiledPath(JPATH_COMPILER);
                List<JsonPath.Match> matches = path.evaluate(node.scopeNode);
                if (matches != null && matches.size() > 0) {
                    if (selector instanceof UrlRewriteFilterBufferDescriptor) {
                        bufferingLevel = node;
                        bufferingConfig = (UrlRewriteFilterBufferDescriptor) selector;
                        buffered = true;
                    }
                    break;
                }
            }
        }
        return buffered;
    }

    protected String filterStreamValue(Level node) {
        String value;
        if (node.isArray()) {
            value = node.node.get(0).asText();
        } else {
            value = node.node.get(node.field).asText();
        }
        String rule = null;
        UrlRewriteFilterGroupDescriptor scope = node.scopeConfig;
        //TODO: Scan the top level apply rules for the first match.
        if (scope != null) {
            for (UrlRewriteFilterPathDescriptor selector : scope.getSelectors()) {
                JsonPath.Expression path = (JsonPath.Expression) selector.compiledPath(JPATH_COMPILER);
                List<JsonPath.Match> matches = path.evaluate(node.scopeNode);
                if (matches != null && matches.size() > 0) {
                    JsonPath.Match match = matches.get(0);
                    if (match.getNode().isTextual()) {
                        if (selector instanceof UrlRewriteFilterApplyDescriptor) {
                            UrlRewriteFilterApplyDescriptor apply = (UrlRewriteFilterApplyDescriptor) selector;
                            rule = apply.rule();
                            break;
                        }
                    }
                }
            }
        }
        try {
            value = filterValueString(node.field, value, rule);
            if (node.isArray()) {
                ((ArrayNode) node.node).set(0, new TextNode(value));
            } else {
                ((ObjectNode) node.node).put(node.field, value);
            }
        } catch (Exception e) {
            LOG.failedToFilterValue(value, rule, e);
        }
        return value;
    }

    private void filterBufferedNode(Level node) {
        for (UrlRewriteFilterPathDescriptor selector : bufferingConfig.getSelectors()) {
            JsonPath.Expression path = (JsonPath.Expression) selector.compiledPath(JPATH_COMPILER);
            List<JsonPath.Match> matches = path.evaluate(node.node);
            for (JsonPath.Match match : matches) {
                if (selector instanceof UrlRewriteFilterApplyDescriptor) {
                    if (match.getNode().isTextual()) {
                        filterBufferedValue(match, (UrlRewriteFilterApplyDescriptor) selector);
                    }
                } else if (selector instanceof UrlRewriteFilterDetectDescriptor) {
                    UrlRewriteFilterDetectDescriptor detectConfig = (UrlRewriteFilterDetectDescriptor) selector;
                    JsonPath.Expression detectPath = (JsonPath.Expression) detectConfig
                            .compiledPath(JPATH_COMPILER);
                    List<JsonPath.Match> detectMatches = detectPath.evaluate(node.node);
                    for (JsonPath.Match detectMatch : detectMatches) {
                        if (detectMatch.getNode().isTextual()) {
                            String detectValue = detectMatch.getNode().asText();
                            Pattern detectPattern = detectConfig.compiledValue(REGEX_COMPILER);
                            if (detectPattern.matcher(detectValue).matches()) {
                                filterBufferedValues(node, detectConfig.getSelectors());
                            }
                        }
                    }
                }
            }
        }
    }

    private void filterBufferedValues(Level node, List<UrlRewriteFilterPathDescriptor> selectors) {
        for (UrlRewriteFilterPathDescriptor selector : selectors) {
            JsonPath.Expression path = (JsonPath.Expression) selector.compiledPath(JPATH_COMPILER);
            List<JsonPath.Match> matches = path.evaluate(node.node);
            for (JsonPath.Match match : matches) {
                if (match.getNode().isTextual()) {
                    if (selector instanceof UrlRewriteFilterApplyDescriptor) {
                        filterBufferedValue(match, (UrlRewriteFilterApplyDescriptor) selector);
                    }
                }
            }
        }
    }

    private void filterBufferedValue(JsonPath.Match match, UrlRewriteFilterApplyDescriptor apply) {
        String field = match.getField();
        String value = match.getNode().asText();
        try {
            value = filterValueString(field, value, apply.rule());
            ((ObjectNode) match.getParent().getNode()).put(field, value);
        } catch (Exception e) {
            LOG.failedToFilterValue(value, apply.rule(), e);
        }
    }

    protected String filterFieldName(String field) {
        return field;
    }

    protected String filterValueString(String name, String value, String rule) {
        return value;
    }

    @Override
    public void close() throws IOException {
        generator.close();
        writer.close();
        parser.close();
        reader.close();
    }

    private static class Level {
        String field;
        JsonNode node;
        JsonNode scopeNode;
        UrlRewriteFilterGroupDescriptor scopeConfig;

        private Level(String field, JsonNode node, JsonNode scopeNode,
                UrlRewriteFilterGroupDescriptor scopeConfig) {
            this.field = field;
            this.node = node;
            this.scopeNode = scopeNode;
            this.scopeConfig = scopeConfig;
        }

        public boolean isArray() {
            return node != null && node.isArray();
        }
    }

    private static class JsonPathCompiler implements UrlRewriteFilterPathDescriptor.Compiler<JsonPath.Expression> {
        @Override
        public JsonPath.Expression compile(String expression, JsonPath.Expression compiled) {
            return JsonPath.compile(expression);
        }
    }

    private static class RegexCompiler implements UrlRewriteFilterPathDescriptor.Compiler<Pattern> {
        @Override
        public Pattern compile(String expression, Pattern compiled) {
            if (compiled != null) {
                return compiled;
            } else {
                return Pattern.compile(expression);
            }
        }
    }

    //  private void dump() throws IOException {
    //    mapper.writeTree( factory.createGenerator( System.out ), stack.get( 0 ).node );
    //    System.out.println();
    //  }

}