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.petstore.springboot;
018
019import org.apache.juneau.petstore.console.*;
020import org.springframework.boot.autoconfigure.*;
021import org.springframework.boot.builder.*;
022import org.springframework.boot.web.servlet.*;
023import org.springframework.context.annotation.*;
024import org.springframework.core.*;
025import org.springframework.stereotype.*;
026
027import jakarta.servlet.*;
028import jakarta.servlet.DispatcherType;
029
030/**
031 * Petstore Spring Boot deployment entry point.
032 *
033 * <p>
034 * Boots a Spring Boot embedded servlet container, wires {@link RootResources} as a Spring bean, and mounts it at
035 * {@code /*} via {@link ServletRegistrationBean}.
036 *
037 * <p>
038 * Provides a {@code @Bean HelloMessageProvider} to demonstrate Spring {@code @Autowired} injection into the
039 * companion {@link HelloResource} child — preserving the deployment-glue Spring-injection demo.
040 *
041 * <h5 class='section'>See Also:</h5><ul>
042 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauPetstore">juneau-petstore</a>
043 * </ul>
044 */
045@SuppressWarnings({
046   "resource" // ApplicationContext is owned by Spring; not a leak.
047})
048@SpringBootApplication
049@Controller
050public class App {
051
052   /**
053    * Entry point.
054    *
055    * @param args Command-line arguments forwarded to Spring Boot.
056    */
057   public static void main(String[] args) {
058      new SpringApplicationBuilder(App.class).run(args);
059   }
060
061   /**
062    * Spring-injected message provider consumed by {@link HelloResource}.
063    *
064    * @return The hello-message provider bean.
065    */
066   @Bean
067   public HelloMessageProvider helloMessageProvider() {
068      return new HelloMessageProvider("Hello from Spring-injected bean!");
069   }
070
071   /**
072    * Returns {@link HelloResource} as a Spring bean with its dependency injected via constructor.
073    *
074    * @param helloMessageProvider The Spring-managed message provider bean.
075    * @return The hello-world REST resource.
076    */
077   @Bean
078   public HelloResource helloResource(HelloMessageProvider helloMessageProvider) {
079      return new HelloResource(helloMessageProvider);
080   }
081
082   /**
083    * Returns the root router as a Spring bean so child resources resolve as Spring beans.
084    *
085    * @return The root router REST resource.
086    */
087   @Bean
088   public RootResources rootResources() {
089      return new RootResources();
090   }
091
092   /**
093    * The API's CSRF boundary: one instance, held here rather than exposed as a {@code Filter} bean so Spring Boot does
094    * not also auto-register it on {@code /*}.  Shared by {@link #consoleBoundary()} and {@link #apiBoundary()}.
095    */
096   private final PetstoreApiBoundary api = new PetstoreApiBoundary();
097
098   /**
099    * Registers the console's CSRF boundary on {@code /console/*}.
100    *
101    * <p>
102    * The {@code /petstore} API is covered by {@link #apiBoundary()}; {@code /petstore-ui} is
103    * outside both.  See {@link PetstoreConsoleBoundary}.  The console boundary is built over the same
104    * {@link PetstoreApiBoundary} the API registration mounts, so the API token its pages carry is one the API filter
105    * accepts.
106    *
107    * <p>
108    * The filter runs on forwarded, async and error dispatches as well as on the original request, so a dispatch that
109    * lands on a console path is checked too.
110    *
111    * @return The filter registration.
112    */
113   @Bean
114   public FilterRegistrationBean<PetstoreConsoleBoundary> consoleBoundary() {
115      var reg = new FilterRegistrationBean<>(new PetstoreConsoleBoundary(api));
116      reg.addUrlPatterns(PetstoreConsoleBoundary.URL_PATTERN);
117      reg.setOrder(Ordered.HIGHEST_PRECEDENCE);
118      reg.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.FORWARD);
119      return reg;
120   }
121
122   /**
123    * Registers the API's CSRF boundary on {@code /petstore/*}.
124    *
125    * <p>
126    * It runs only on the original request: an internal forward or error dispatch to the API is not a new
127    * attacker-controlled request.  The order keeps it just behind the console boundary; the two patterns never overlap.
128    *
129    * @return The filter registration.
130    */
131   @Bean
132   public FilterRegistrationBean<PetstoreApiBoundary> apiBoundary() {
133      var reg = new FilterRegistrationBean<>(api);
134      reg.addUrlPatterns(PetstoreApiBoundary.URL_PATTERN);
135      reg.setOrder(Ordered.HIGHEST_PRECEDENCE + 10);
136      reg.setDispatcherTypes(DispatcherType.REQUEST);
137      return reg;
138   }
139
140   /**
141    * Mounts the root router servlet at {@code /*}.
142    *
143    * @param rootResources The Spring-managed root router.
144    * @return The servlet registration mapped to {@code /*}.
145    */
146   @Bean
147   public ServletRegistrationBean<Servlet> rootServlet(RootResources rootResources) {
148      return new ServletRegistrationBean<>(rootResources, "/*");
149   }
150}