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