001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2010–2011 ApexIdentity Inc. 015 * Portions Copyright 2011-2014 ForgeRock AS. 016 */ 017 018package org.forgerock.openig.filter; 019 020import static org.forgerock.openig.util.Json.*; 021 022import java.io.IOException; 023import java.io.Reader; 024import java.nio.charset.Charset; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028 029import org.forgerock.json.fluent.JsonValue; 030import org.forgerock.json.fluent.JsonValueException; 031import org.forgerock.openig.el.Expression; 032import org.forgerock.openig.handler.Handler; 033import org.forgerock.openig.handler.HandlerException; 034import org.forgerock.openig.heap.GenericHeaplet; 035import org.forgerock.openig.heap.HeapException; 036import org.forgerock.openig.http.Exchange; 037import org.forgerock.openig.http.Message; 038import org.forgerock.openig.http.MessageType; 039import org.forgerock.openig.regex.PatternTemplate; 040import org.forgerock.openig.regex.StreamPatternExtractor; 041 042/** 043 * Extracts regular expression patterns from a message entity. Extraction occurs either 044 * before the exchange is handled if {@code messageType} is {@link MessageType#REQUEST}, or 045 * after the exchange is handled if it is {@link MessageType#RESPONSE}. Each pattern can have 046 * an associated template, which is applied to its match result. 047 * <p> 048 * The extraction results are contained in a {@link Map} object, whose location is specified 049 * by the {@code target} expression. For a given matched pattern, the value stored in the map 050 * is either the result of applying its associated pattern template (if specified) or the 051 * match result itself otherwise. 052 * 053 * @see StreamPatternExtractor 054 * @see PatternTemplate 055 */ 056public class EntityExtractFilter extends GenericFilter { 057 058 /** Extracts regular expression patterns from entities. */ 059 private final StreamPatternExtractor extractor = new StreamPatternExtractor(); 060 061 /** The message type in the exchange to extract patterns from. */ 062 private final MessageType messageType; 063 064 /** Overrides the character set encoding specified in message. If {@code null}, the message encoding is used. */ 065 private final Charset charset; 066 067 /** Expression that yields the target object that will contain the mapped extraction results. */ 068 private final Expression target; 069 070 /** 071 * Builds an EntityExtractFilter that will act either on {@link MessageType#REQUEST} or {@link MessageType#RESPONSE} 072 * flow, extracting patterns into the given {@code target} {@link Expression}. The {@link Charset} used is the one 073 * of the message. 074 * 075 * @param type 076 * Specifies the execution flow to be executed in 077 * @param target 078 * Expression that yields the target object that will contain the mapped extraction results 079 */ 080 public EntityExtractFilter(final MessageType type, final Expression target) { 081 this(type, target, null); 082 } 083 084 /** 085 * Builds an EntityExtractFilter that will act either on {@link MessageType#REQUEST} or {@link MessageType#RESPONSE} 086 * flow, extracting patterns into the given {@code target} {@link Expression}. The {@link Charset} used is the one 087 * specified. 088 * 089 * @param type 090 * Specifies the execution flow to be executed in 091 * @param target 092 * Expression that yields the target object that will contain the mapped extraction results 093 * @param charset 094 * Overrides the character set encoding specified in message. If {@code null}, the message encoding is used 095 */ 096 public EntityExtractFilter(final MessageType type, final Expression target, final Charset charset) { 097 this.messageType = type; 098 this.target = target; 099 this.charset = charset; 100 } 101 102 /** 103 * Returns the regular expression patterns extractor. 104 * @return the regular expression patterns extractor. 105 */ 106 public StreamPatternExtractor getExtractor() { 107 return extractor; 108 } 109 110 @Override 111 public void filter(Exchange exchange, Handler next) throws HandlerException, IOException { 112 if (messageType == MessageType.REQUEST) { 113 process(exchange, exchange.request); 114 } 115 next.handle(exchange); 116 if (messageType == MessageType.RESPONSE) { 117 process(exchange, exchange.response); 118 } 119 } 120 121 private void process(Exchange exchange, Message<?> message) { 122 HashMap<String, String> map = new HashMap<String, String>(); 123 if (message != null) { 124 try { 125 Reader reader = message.getEntity().newDecodedContentReader(charset); 126 try { 127 // get 'em all now 128 for (Map.Entry<String, String> match : extractor.extract(reader)) { 129 map.put(match.getKey(), match.getValue()); 130 } 131 } finally { 132 reader.close(); 133 } 134 } catch (IOException ioe) { 135 // may yield partial or unresolved attributes 136 } 137 } 138 target.set(exchange, map); 139 } 140 141 /** Creates and initializes an entity extract handler in a heap environment. */ 142 public static class Heaplet extends GenericHeaplet { 143 @Override 144 public Object create() throws HeapException { 145 EntityExtractFilter filter = new EntityExtractFilter(config.get("messageType") 146 .required() 147 .asEnum(MessageType.class), 148 asExpression(config.get("target").required()), 149 config.get("charset").asCharset()); 150 151 for (JsonValue jv : config.get("bindings").required().expect(List.class)) { 152 jv.required().expect(Map.class); 153 String key = jv.get("key").required().asString(); 154 if (filter.extractor.getPatterns().containsKey(key)) { 155 throw new JsonValueException(jv.get("key"), "Key already defined"); 156 } 157 filter.extractor.getPatterns().put(key, jv.get("pattern").required().asPattern()); 158 String template = jv.get("template").asString(); 159 if (template != null) { 160 filter.extractor.getTemplates().put(key, new PatternTemplate(template)); 161 } 162 } 163 return filter; 164 } 165 } 166}