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.header; 018 019import static org.apache.juneau.common.utils.StringUtils.*; 020import static org.apache.juneau.common.utils.Utils.*; 021import static org.apache.juneau.internal.ClassUtils.*; 022 023import java.util.*; 024import java.util.stream.*; 025 026import org.apache.juneau.common.utils.*; 027import org.apache.juneau.http.annotation.*; 028 029/** 030 * Represents a parsed <l>Thrown</l> HTTP response header. 031 * 032 * <p> 033 * Contains exception information including name and optionally a message. 034 * 035 * <h5 class='figure'>Example</h5> 036 * <p class='bcode'> 037 * Thrown: org.apache.juneau.http.response.NotFound;Resource was not found 038 * </p> 039 * 040 * <p> 041 * This header isn't part of the RFC2616 specification, but is provided to allow for Java exception information 042 * to be delivered to remote REST Java interfaces. 043 * 044 * <p> 045 * This header supports comma-delimited values for multiple thrown values. 046 * <p class='bcode'> 047 * Thrown: org.apache.juneau.http.response.NotFound;Resource was not found,java.lang.RuntimeException;foo 048 * </p> 049 * 050 * <p> 051 * Note that this is equivalent to specifying multiple header values. 052 * <p class='bcode'> 053 * Thrown: org.apache.juneau.http.response.NotFound;Resource was not found 054 * Thrown: java.lang.RuntimeException;foo 055 * </p> 056 * 057 * <h5 class='section'>See Also:</h5><ul> 058 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommonBasics">juneau-rest-common Basics</a> 059 * </ul> 060 * 061 * @serial exclude 062 */ 063@Header("Thrown") 064public class Thrown extends BasicCsvHeader { 065 066 //----------------------------------------------------------------------------------------------------------------- 067 // Static 068 //----------------------------------------------------------------------------------------------------------------- 069 070 private static final long serialVersionUID = 1L; 071 private static final String NAME = "Thrown"; 072 073 /** 074 * An empty unmodifiable Thrown header. 075 */ 076 public static final Thrown EMPTY = new Thrown((String)null); 077 078 /** 079 * Static creator. 080 * 081 * @param value 082 * The header value. 083 * <br>Can be <jk>null</jk>. 084 * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>. 085 */ 086 public static Thrown of(String value) { 087 return value == null ? null : new Thrown(value); 088 } 089 090 /** 091 * Static creator. 092 * 093 * @param values 094 * The header value. 095 * <br>Can be <jk>null</jk>. 096 * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>. 097 */ 098 public static Thrown of(Throwable...values) { 099 return new Thrown(alist(values).stream().map(Part::new).collect(Collectors.toList())); 100 } 101 102 //----------------------------------------------------------------------------------------------------------------- 103 // Instance 104 //----------------------------------------------------------------------------------------------------------------- 105 106 private final List<Part> value; 107 108 /** 109 * Constructor. 110 * 111 * @param value 112 * The header value. 113 */ 114 public Thrown(String value) { 115 super(NAME, value); 116 List<Part> l = Utils.list(); 117 Utils.split(value, x -> l.add(new Part(x))); 118 this.value = value == null ? null : u(l); 119 } 120 121 /** 122 * Constructor. 123 * 124 * @param value 125 * The header value. 126 */ 127 public Thrown(List<Part> value) { 128 super(NAME, Utils.join(value, ", ")); 129 this.value = u(value); 130 } 131 132 /** 133 * Returns the class name portion of the header. 134 * 135 * @return The class name portion of the header, or <jk>null</jk> if not there. 136 */ 137 public Optional<List<Part>> asParts() { 138 return Utils.opt(value); 139 } 140 141 /** 142 * Represents a single entry in this header. 143 */ 144 public static class Part { 145 146 String className, message; 147 String value; 148 149 /** 150 * Constructor. 151 * 152 * @param value The header part value. 153 */ 154 public Part(String value) { 155 this.value = value; 156 int i = value.indexOf(';'); 157 this.className = urlDecode(i == -1 ? value.trim() : value.substring(0, i).trim()); 158 this.message = urlDecode(i == -1 ? null : value.substring(i+1).trim()); 159 } 160 161 /** 162 * Constructor. 163 * 164 * @param value The throwable to create the header part value from. 165 */ 166 public Part(Throwable value) { 167 this.className = className(value); 168 this.message = value.getMessage(); 169 this.value = urlEncode(className) + ';' + urlEncode(value.getMessage()); 170 } 171 172 /** 173 * Returns the message portion of the header. 174 * 175 * @return The message portion of the header, or <jk>null</jk> if not there. 176 */ 177 public String getClassName() { 178 return className; 179 } 180 181 /** 182 * Returns the message portion of the header. 183 * 184 * @return The message portion of the header, or <jk>null</jk> if not there. 185 */ 186 public String getMessage() { 187 return message; 188 } 189 190 @Override /* Object */ 191 public String toString() { 192 return value; 193 } 194 } 195}