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.commons.utils.CollectionUtils.*;
020import static org.apache.juneau.commons.utils.StringUtils.*;
021import static org.apache.juneau.commons.utils.Utils.*;
022
023import java.util.*;
024import java.util.stream.*;
025
026import org.apache.juneau.commons.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    * Represents a single entry in this header.
067    */
068   public static class Part {
069
070      String className, message;
071      String value;
072
073      /**
074       * Constructor.
075       *
076       * @param value The header part value.
077      */
078      public Part(String value) {
079         this.value = value;
080         var i = value.indexOf(';');
081         this.className = urlDecode(i == -1 ? value.trim() : value.substring(0, i).trim());
082         this.message = urlDecode(i == -1 ? null : value.substring(i + 1).trim());
083      }
084
085      /**
086       * Constructor.
087       *
088       * @param value The throwable to create the header part value from.
089       */
090      public Part(Throwable value) {
091         this.className = cn(value);
092         this.message = value.getMessage();
093         this.value = urlEncode(className) + ';' + urlEncode(value.getMessage());
094      }
095
096      /**
097       * Returns the message portion of the header.
098       *
099       * @return The message portion of the header, or <jk>null</jk> if not there.
100       */
101      public String getClassName() { return className; }
102
103      /**
104       * Returns the message portion of the header.
105       *
106       * @return The message portion of the header, or <jk>null</jk> if not there.
107       */
108      public String getMessage() { return message; }
109
110      @Override /* Overridden from Object */
111      public String toString() {
112         return value;
113      }
114   }
115
116   private static final long serialVersionUID = 1L;
117
118   private static final String NAME = "Thrown";
119
120   /**
121    * An empty unmodifiable Thrown header.
122    */
123   public static final Thrown EMPTY = new Thrown((String)null);
124
125   /**
126    * Static creator.
127    *
128    * @param value
129    *    The header value.
130    *    <br>Can be <jk>null</jk>.
131    * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>.
132    */
133   public static Thrown of(String value) {
134      return value == null ? null : new Thrown(value);
135   }
136
137   /**
138    * Static creator.
139    *
140    * @param values
141    *    The header value.
142    *    <br>Can be <jk>null</jk>.
143    * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>.
144    */
145   public static Thrown of(Throwable...values) {
146      return new Thrown(l(values).stream().map(Part::new).collect(Collectors.toList()));
147   }
148
149   private final List<Part> value;
150
151   /**
152    * Constructor.
153    *
154    * @param value
155    *    The header value.
156    */
157   public Thrown(List<Part> value) {
158      super(NAME, StringUtils.join(value, ", "));
159      this.value = u(value);
160   }
161
162   /**
163    * Constructor.
164    *
165    * @param value
166    *    The header value.
167    */
168   public Thrown(String value) {
169      super(NAME, value);
170      List<Part> l = list();
171      StringUtils.split(value, x -> l.add(new Part(x)));
172      this.value = value == null ? null : u(l);
173   }
174
175   /**
176    * Returns the class name portion of the header.
177    *
178    * @return The class name portion of the header, or <jk>null</jk> if not there.
179    */
180   public Optional<List<Part>> asParts() {
181      return opt(value);
182   }
183}