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.rest.server.convention;
018
019import org.apache.juneau.http.*;
020import org.apache.juneau.rest.server.*;
021import org.apache.juneau.rest.server.servlet.*;
022
023/**
024 * Mixin that serves a {@code favicon.ico} icon at {@code /favicon.ico}.
025 *
026 * <p>
027 * Sibling of {@link SeoMixin} (robots.txt / sitemap.xml), {@link VersionMixin}
028 * ({@code /version}), and {@link WellKnownMixin}
029 * ({@code /.well-known/*}). All four classes live in the {@code org.apache.juneau.rest.server.convention}
030 * convention-endpoints mixin pack.
031 *
032 * <p>
033 * Compose into a host resource via {@link Rest#mixins() @Rest(mixins=FaviconMixin.class)};
034 * the {@code /favicon.ico} URL becomes available alongside the host's own endpoints with no further
035 * wiring.
036 *
037 * <h5 class='section'>Hardcoded mount path:</h5>
038 *
039 * <p>
040 * Unlike the sibling api-docs and ops mixins (see {@link org.apache.juneau.rest.server.docs.SwaggerMixin},
041 * {@link VersionMixin}, etc.) the mount path here is <b>not</b> SVL-configurable
042 * &mdash; {@code /favicon.ico} is fixed by browser convention and the {@code link rel="icon"}
043 * default the HTML spec inherits from <a href="https://html.spec.whatwg.org/multipage/links.html#rel-icon">WHATWG HTML</a>.
044 * Browsers fetch {@code /favicon.ico} from the site root regardless of any application-level
045 * routing rewrites, so a runtime mount-path override here would have no practical effect.
046 *
047 * <h5 class='section'>Mixin-only deployment:</h5>
048 *
049 * <p>
050 * This resource is designed for composition via {@code @Rest(mixins=...)}. The mount path is
051 * pinned at the op level by {@link RestGet @RestGet(path="/favicon.ico")} on {@link #getFavicon};
052 * a class-level {@code @Rest(paths=...)} declaration would be silently ignored under the mixin
053 * pattern (see {@link Rest#paths() @Rest(paths)} Javadoc).
054 *
055 * <h5 class='figure'>Composition example:</h5>
056 *
057 * <p class='bjava'>
058 *    <ja>@Rest</ja>(path=<js>"/api"</js>, mixins=FaviconMixin.<jk>class</jk>)
059 *    <jk>public class</jk> ApiResource <jk>extends</jk> RestServlet {
060 *       <jc>// Use the default Juneau-branded favicon.</jc>
061 *    }
062 *
063 *    <jc>// Or override the icon bytes via a @Bean factory:</jc>
064 *    <ja>@Bean FaviconMixin favicon()</ja> {
065 *       <jk>return</jk> FaviconMixin.<jsm>create</jsm>().bytes(myLogoBytes).build();
066 *    }
067 * </p>
068 *
069 * <h5 class='section'>Behavior:</h5>
070 *
071 * <ul class='spaced-list'>
072 *    <li>{@code GET /favicon.ico} returns the configured icon bytes with
073 *       {@code Content-Type: image/x-icon} and {@code Cache-Control: max-age=2592000, public}
074 *       (30 days &mdash; favicons rarely change and browsers re-fetch frequently when uncached).
075 *    <li>The default icon ({@code juneau-favicon.ico} on the framework classpath) is a
076 *       16&times;16 Juneau-branded ICO; users replace it by registering an alternate
077 *       {@code @Bean FaviconMixin} whose builder supplies different bytes.
078 *    <li>The handler is excluded from generated Swagger / OpenAPI specs via
079 *       {@link OpSwagger#ignore() @OpSwagger(ignore=true)} &mdash; favicons are not
080 *       API-meaningful.
081 * </ul>
082 *
083 * <h5 class='section'>Builder API:</h5>
084 *
085 * <ul class='spaced-list'>
086 *    <li>{@link #create() create()} &mdash; entry point for configuring an instance.
087 *    <li>{@link Builder#bytes(byte[]) bytes(byte[])} &mdash; raw favicon bytes.
088 *    <li>{@link Builder#classpath(String) classpath(String)} &mdash; load icon bytes from a
089 *       classpath resource (resolved against the {@code FaviconMixin} classloader).
090 *    <li>{@link Builder#cacheControl(String) cacheControl(String)} &mdash; override the default
091 *       30-day {@code Cache-Control} header.
092 * </ul>
093 *
094 * <h5 class='section'>See Also:</h5><ul>
095 *    <li class='jc'>{@link SeoMixin}
096 *    <li class='jc'>{@link VersionMixin}
097 *    <li class='jc'>{@link WellKnownMixin}
098 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerComposition">REST Server — Mixins and Multi-Mount Paths</a>
099 * </ul>
100 *
101 * @since 10.0.0
102 */
103// @formatter:off
104@Rest
105public class FaviconMixin extends RestMixin {
106
107   /** Default {@code Cache-Control} header value: {@code max-age=2592000, public} (30 days). */
108   public static final String DEFAULT_CACHE_CONTROL = FaviconProvider.DEFAULT_CACHE_CONTROL;
109
110   /**
111    * Creates a new builder for configuring a {@link FaviconMixin}.
112    *
113    * @return A new builder.
114    */
115   public static Builder create() {
116      return new Builder();
117   }
118
119   private final FaviconProvider worker;
120
121   /**
122    * No-arg constructor &mdash; used when a host registers the mixin without supplying a
123    * builder-configured {@code @Bean FaviconMixin}. Delegates to a default {@link FaviconProvider} worker
124    * (the framework-shipped Juneau-branded favicon).
125    */
126   public FaviconMixin() {
127      this(new FaviconProvider());
128   }
129
130   /**
131    * Worker constructor.
132    *
133    * @param worker The shared {@link FaviconProvider} worker this flavor delegates to. Must not be
134    *    <jk>null</jk>.
135    */
136   protected FaviconMixin(FaviconProvider worker) {
137      this.worker = worker;
138   }
139
140   /**
141    * Worker + builder constructor.
142    *
143    * <p>
144    * Used by {@link Builder#build()} to both delegate to the shared {@link FaviconProvider} worker and stash the
145    * programmatic {@link RestBuilder}(carrying any {@code @Rest}-level overrides such as {@code path}) so those
146    * values take precedence over the mixin class's own {@link Rest @Rest} annotation.
147    *
148    * <h5 class='section'>Why worker + builder, not the flavor Builder:</h5>
149    * <ul class='spaced-list'>
150    *    <li>Takes the already-built <b>worker bean</b> (not the flavor {@link Builder}) so the flavor can be
151    *       constructed from ANY independently-supplied worker &mdash; e.g. a user's own {@code @Bean FaviconProvider}
152    *       or BeanStore bean, per the delegate-bean model &mdash; not only via this flavor's own builder.
153    *    <li>Takes the generic {@link RestBuilder}(here {@code this} from {@link Builder#build()}) so it honors the
154    *       uniform &sect;2.4 {@code Foo(RestBuilder<?>)} injection contract the base class and DI resolution key on; the
155    *       base knows nothing about the concrete flavor builder or the worker type.
156    *    <li>Holds the finished worker product (the {@code final} {@link FaviconProvider} field), not a transient
157    *       builder; the worker is materialized exactly once at {@link Builder#build()} time.
158    *    <li>Keeps the capability worker and the REST-level config as two distinct inputs.
159    * </ul>
160    *
161    * @param worker The shared {@link FaviconProvider} worker this flavor delegates to. Must not be <jk>null</jk>.
162    * @param builder The programmatic configuration builder. May be <jk>null</jk>.
163    */
164   protected FaviconMixin(FaviconProvider worker, RestBuilder<?> builder) {
165      super(builder);
166      this.worker = worker;
167   }
168
169   /**
170    * [GET /favicon.ico] &mdash; serve the configured favicon bytes.
171    *
172    * @return The favicon as an {@link HttpResource} with proper headers.
173    */
174   @RestGet(
175      path="/favicon.ico",
176      summary="Favorites icon",
177      description="Browser favorites icon (favicon.ico).",
178      swagger=@OpSwagger(ignore=true)
179   )
180   public HttpResource getFavicon() {
181      return worker.serve();
182   }
183
184   /**
185    * Builder for {@link FaviconMixin} instances.
186    *
187    * <p>
188    * Mirrors {@link FaviconProvider.Builder}'s configuration methods on the mixin's own surface and
189    * forwards each call to an underlying {@link FaviconProvider.Builder}, which builds the shared worker
190    * the mixin delegates to.
191    *
192    * <p>
193    * Extends {@link org.apache.juneau.rest.server.servlet.RestMixin.Builder RestMixin.Builder} so the mixin's bespoke worker-config setters
194    * ({@link #bytes(byte[])}, {@link #classpath(String)}, {@link #cacheControl(String)}) chain with true
195    * covariant returns alongside the inherited {@link RestBuilder}surface (e.g. {@code path}, {@code roleGuard}).
196    * This is how a multi-flavor capability avoids triplicating its REST-level config: the worker config is
197    * forwarded once into {@link FaviconProvider.Builder}, and the REST config is inherited once from
198    * {@link AbstractRestBuilder}.
199    */
200   public static class Builder extends RestMixin.Builder<FaviconMixin,Builder> {
201
202      private final FaviconProvider.Builder worker = FaviconProvider.create();
203
204      /** Constructor &mdash; package access for {@link FaviconMixin#create()}. */
205      protected Builder() {
206         super(FaviconMixin.class);
207      }
208
209      /**
210       * Sets the raw favicon bytes.
211       *
212       * <p>
213       * Mutually exclusive with {@link #classpath(String)} &mdash; whichever is set last wins
214       * at {@link #build()} time.
215       *
216       * @param value The favicon bytes (typically an {@code .ico} or {@code .png} payload).
217       *    Must not be <jk>null</jk>.
218       * @return This object.
219       */
220      public Builder bytes(byte[] value) {
221         worker.bytes(value);
222         return this;
223      }
224
225      /**
226       * Sets the classpath resource path from which to load the favicon bytes.
227       *
228       * <p>
229       * A resolved-to-{@code null} stream falls back to the framework's default favicon.
230       * Mutually exclusive with {@link #bytes(byte[])}.
231       *
232       * @param value The classpath resource path (e.g. {@code "/myapp/icon.ico"}).
233       *    Must not be <jk>null</jk> or blank.
234       * @return This object.
235       */
236      public Builder classpath(String value) {
237         worker.classpath(value);
238         return this;
239      }
240
241      /**
242       * Sets the {@code Cache-Control} header value emitted with the favicon response.
243       *
244       * <p>
245       * Defaults to {@value #DEFAULT_CACHE_CONTROL} (30 days).
246       *
247       * @param value The new {@code Cache-Control} value. Must not be <jk>null</jk> or blank.
248       * @return This object.
249       */
250      public Builder cacheControl(String value) {
251         worker.cacheControl(value);
252         return this;
253      }
254
255      /**
256       * Builds a {@link FaviconMixin} instance, delegating to the shared {@link FaviconProvider} worker and
257       * stashing this builder so its {@code @Rest}-level overrides take precedence over the annotation.
258       *
259       * @return A configured instance.
260       */
261      @Override /* AbstractRestBuilder */
262      public FaviconMixin build() {
263         return new FaviconMixin(worker.build(), this);
264      }
265   }
266}