001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.http.classic.header; 018 019import static org.apache.juneau.commons.utils.CollectionUtils.*; 020import static org.apache.juneau.commons.utils.Shorts.*; 021import static org.apache.juneau.commons.utils.StringUtils.*; 022 023import java.util.*; 024 025import org.apache.juneau.commons.utils.*; 026import org.apache.juneau.http.*; 027 028/** 029 * Represents a parsed <l>Thrown</l> HTTP response header. 030 * 031 * <p> 032 * Contains exception information including name and optionally a message. 033 * 034 * <h5 class='figure'>Example</h5> 035 * <p class='bcode'> 036 * Thrown: org.apache.juneau.http.classic.response.NotFound;Resource was not found 037 * </p> 038 * 039 * <p> 040 * This header isn't part of the RFC2616 specification, but is provided to allow for Java exception information 041 * to be delivered to remote REST Java interfaces. 042 * 043 * <p> 044 * This header supports comma-delimited values for multiple thrown values. 045 * <p class='bcode'> 046 * Thrown: org.apache.juneau.http.classic.response.NotFound;Resource was not found,RuntimeException;foo 047 * </p> 048 * 049 * <p> 050 * Note that this is equivalent to specifying multiple header values. 051 * <p class='bcode'> 052 * Thrown: org.apache.juneau.http.classic.response.NotFound;Resource was not found 053 * Thrown: RuntimeException;foo 054 * </p> 055 * 056 * <h5 class='section'>See Also:</h5><ul> 057 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommon">juneau-rest-common</a> 058 * </ul> 059 * 060 * @serial exclude 061 */ 062@Header("Thrown") 063@SuppressWarnings({ 064 "java:S2160", // equals() inherited from BasicHeader compares name+value; no additional equality-relevant fields 065 "java:S9149" // Public Juneau DSL factory; hiding parent of()/create() is intentional. 066}) 067public class Thrown extends BasicCsvHeader { 068 069 /** 070 * Represents a single entry in this header. 071 */ 072 public static class Part { 073 074 String className; 075 String message; 076 String value; 077 078 /** 079 * Constructor. 080 * 081 * @param value The header part value. 082 */ 083 public Part(String value) { 084 this.value = value; 085 var i = value.indexOf(';'); 086 this.className = urlDecode(i == -1 ? value.trim() : value.substring(0, i).trim()); 087 this.message = urlDecode(i == -1 ? null : value.substring(i + 1).trim()); 088 } 089 090 /** 091 * Constructor. 092 * 093 * @param value The throwable to create the header part value from. 094 */ 095 public Part(Throwable value) { 096 this.className = UnmodifiableBean.logicalType(value).getName(); 097 this.message = value.getMessage(); 098 this.value = urlEncode(className) + ';' + urlEncode(value.getMessage()); 099 } 100 101 /** 102 * Returns the message portion of the header. 103 * 104 * @return The message portion of the header, or <jk>null</jk> if not there. 105 */ 106 public String getClassName() { return className; } 107 108 /** 109 * Returns the message portion of the header. 110 * 111 * @return The message portion of the header, or <jk>null</jk> if not there. 112 */ 113 public String getMessage() { return message; } 114 115 @Override /* Overridden from Object */ 116 public String toString() { 117 return value; 118 } 119 } 120 121 private static final long serialVersionUID = 1L; 122 123 private static final String NAME = "Thrown"; 124 125 /** 126 * An empty unmodifiable Thrown header. 127 */ 128 public static final Thrown EMPTY = new Thrown((String)null); 129 130 /** 131 * Static creator. 132 * 133 * @param value 134 * The header value. 135 * <br>Can be <jk>null</jk>. 136 * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>. 137 */ 138 public static Thrown of(String value) { 139 return value == null ? null : new Thrown(value); 140 } 141 142 /** 143 * Static creator. 144 * 145 * @param values 146 * The header value. 147 * <br>Can be <jk>null</jk> or empty (treated as no thrown values). 148 * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>. 149 */ 150 public static Thrown of(Throwable...values) { 151 return new Thrown(l(values).stream().map(Part::new).toList()); 152 } 153 154 /** 155 * Static creator. 156 * 157 * @param value 158 * The header value. 159 * <br>Can be <jk>null</jk> or empty to create a header with no value. 160 * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>. 161 */ 162 public static Thrown of(String...value) { 163 return value == null ? null : new Thrown(value); 164 } 165 166 /** 167 * Static creator for two CSV tokens. 168 * 169 * <p> 170 * This distinct fixed-arity overload ensures two literal {@link String} arguments resolve to this class's own 171 * factory (returning a {@link Thrown}) rather than the inherited fixed-arity {@link BasicCsvHeader#of(String,String)}, 172 * which Java overload resolution would otherwise prefer. 173 * 174 * @param value1 The first CSV token. 175 * @param value2 The second CSV token. 176 * @return A new header bean. 177 */ 178 public static Thrown of(String value1, String value2) { 179 return new Thrown(value1, value2); 180 } 181 182 private final transient List<Part> value; 183 184 /** 185 * Constructor. 186 * 187 * @param value 188 * The header value. 189 */ 190 public Thrown(List<Part> value) { 191 super(NAME, StringUtils.join(value, ", ")); 192 this.value = u(value); 193 } 194 195 /** 196 * Constructor. 197 * 198 * @param value 199 * The header value. 200 */ 201 public Thrown(String value) { 202 super(NAME, value); 203 List<Part> l = list(); 204 StringUtils.split(value, x -> l.add(new Part(x))); 205 this.value = value == null ? null : u(l); 206 } 207 208 /** 209 * Constructor. 210 * 211 * @param value 212 * The header value. 213 * <br>Can be <jk>null</jk> or empty to create a header with no value. 214 */ 215 public Thrown(String...value) { 216 this(value == null ? null : join(value, ", ")); 217 } 218 219 /** 220 * Returns the parts in this header. 221 * 222 * @return The parts in this header, or an empty {@link Optional} if the header value was <jk>null</jk>. 223 */ 224 public Optional<List<Part>> asParts() { 225 return o(value); 226 } 227}