Java tutorial
/** * 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(); // } }