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}