001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.rest.client; 014 015import java.io.*; 016import java.util.regex.*; 017 018/** 019 * Used to find regular expression matches in REST responses made through {@link RestCall}. 020 * 021 * <p> 022 * Response patterns are applied to REST calls through the {@link RestCall#responsePattern(ResponsePattern)} method. 023 * 024 * <h5 class='section'>Example:</h5> 025 * 026 * This example shows how to use a response pattern finder to find and capture patterns for <js>"x=number"</js> and 027 * <js>"y=string"</js> from a response body. 028 * <p class='bcode'> 029 * <jk>final</jk> List<Number> xList = <jk>new</jk> ArrayList<Number>(); 030 * <jk>final</jk> List<String> yList = <jk>new</jk> ArrayList<String>(); 031 * 032 * restClient.doGet(<jsf>URL</jsf>) 033 * .addResponsePattern( 034 * <jk>new</jk> ResponsePattern(<js>"x=(\\d+)"</js>) { 035 * <ja>@Override</ja> 036 * <jk>public void</jk> onMatch(RestCall restCall, Matcher m) <jk>throws</jk> RestCallException { 037 * xList.add(Integer.<jsm>parseInt</jsm>(m.group(1))); 038 * } 039 * <ja>@Override</ja> 040 * <jk>public void</jk> onNoMatch(RestCall restCall) <jk>throws</jk> RestCallException { 041 * <jk>throw new</jk> RestCallException(<js>"No X's found!"</js>); 042 * } 043 * } 044 * ) 045 * .addResponsePattern( 046 * <jk>new</jk> ResponsePattern(<js>"y=(\\S+)"</js>) { 047 * <ja>@Override</ja> 048 * <jk>public void</jk> onMatch(RestCall restCall, Matcher m) <jk>throws</jk> RestCallException { 049 * yList.add(m.group(1)); 050 * } 051 * <ja>@Override</ja> 052 * <jk>public void</jk> onNoMatch(RestCall restCall) <jk>throws</jk> RestCallException { 053 * <jk>throw new</jk> RestCallException(<js>"No Y's found!"</js>); 054 * } 055 * } 056 * ) 057 * .run(); 058 * </p> 059 * 060 * <h5 class='section'>Important Notes:</h5> 061 * <ol class='spaced-list'> 062 * <li> 063 * Using response patterns does not affect the functionality of any of the other methods 064 * used to retrieve the response such as {@link RestCall#getResponseAsString()} or {@link RestCall#getResponse(Class)}. 065 * <br>HOWEVER, if you want to retrieve the entire text of the response from inside the match methods, use 066 * {@link RestCall#getCapturedResponse()} since this method will not absorb the response for those other methods. 067 * <li> 068 * Response pattern methods are NOT executed if a REST exception occurs during the request. 069 * <li> 070 * The {@link RestCall#successPattern(String)} and {@link RestCall#failurePattern(String)} methods use instances 071 * of this class to throw {@link RestCallException RestCallExceptions} when success patterns are not found or 072 * failure patterns are found. 073 * <li> 074 * {@link ResponsePattern} objects are reusable and thread-safe. 075 * </ol> 076 */ 077public abstract class ResponsePattern { 078 079 private Pattern pattern; 080 081 /** 082 * Constructor. 083 * 084 * @param pattern Regular expression pattern. 085 */ 086 public ResponsePattern(String pattern) { 087 this.pattern = Pattern.compile(pattern); 088 } 089 090 void match(RestCall rc) throws RestCallException { 091 try { 092 Matcher m = pattern.matcher(rc.getCapturedResponse()); 093 boolean found = false; 094 while (m.find()) { 095 onMatch(rc, m); 096 found = true; 097 } 098 if (! found) 099 onNoMatch(rc); 100 } catch (IOException e) { 101 throw new RestCallException(e); 102 } 103 } 104 105 /** 106 * Returns the pattern passed in through the constructor. 107 * 108 * @return The pattern passed in through the constructor. 109 */ 110 protected String getPattern() { 111 return pattern.pattern(); 112 } 113 114 /** 115 * Instances can override this method to handle when a regular expression pattern matches on the output. 116 * 117 * <p> 118 * This method is called once for every pattern match that occurs in the response text. 119 * 120 * @param rc The {@link RestCall} that this pattern finder is being used on. 121 * @param m The regular expression {@link Matcher}. Can be used to retrieve group matches in the pattern. 122 * @throws RestCallException Instances can throw an exception if a failure condition is detected. 123 */ 124 public void onMatch(RestCall rc, Matcher m) throws RestCallException {} 125 126 /** 127 * Instances can override this method to handle when a regular expression pattern doesn't match on the output. 128 * 129 * @param rc The {@link RestCall} that this pattern finder is being used on. 130 * @throws RestCallException Instances can throw an exception if a failure condition is detected. 131 */ 132 public void onNoMatch(RestCall rc) throws RestCallException {} 133}