DefaultResourceServer.java

  1. /*
  2.  * *********************************************************************************************************************
  3.  *
  4.  * blueMarine II: Semantic Media Centre
  5.  * http://tidalwave.it/projects/bluemarine2
  6.  *
  7.  * Copyright (C) 2015 - 2021 by Tidalwave s.a.s. (http://tidalwave.it)
  8.  *
  9.  * *********************************************************************************************************************
  10.  *
  11.  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  12.  * the License. You may obtain a copy of the License at
  13.  *
  14.  *     http://www.apache.org/licenses/LICENSE-2.0
  15.  *
  16.  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  17.  * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
  18.  * specific language governing permissions and limitations under the License.
  19.  *
  20.  * *********************************************************************************************************************
  21.  *
  22.  * git clone https://bitbucket.org/tidalwave/bluemarine2-src
  23.  * git clone https://github.com/tidalwave-it/bluemarine2-src
  24.  *
  25.  * *********************************************************************************************************************
  26.  */
  27. package it.tidalwave.bluemarine2.rest.impl.server;

  28. import javax.annotation.Nonnull;
  29. import javax.inject.Inject;
  30. import java.util.stream.Stream;
  31. import java.util.Enumeration;
  32. import java.util.EnumSet;
  33. import java.io.IOException;
  34. import java.net.InetAddress;
  35. import java.net.Inet4Address;
  36. import java.net.InetSocketAddress;
  37. import java.net.NetworkInterface;
  38. import java.net.SocketException;
  39. import javax.servlet.DispatcherType;
  40. import it.tidalwave.util.annotation.VisibleForTesting;
  41. import org.eclipse.jetty.util.resource.Resource;
  42. import org.eclipse.jetty.util.resource.ResourceCollection;
  43. import org.eclipse.jetty.server.Server;
  44. import org.eclipse.jetty.server.ServerConnector;
  45. import org.eclipse.jetty.servlet.FilterHolder;
  46. import org.eclipse.jetty.servlet.ServletContextHandler;
  47. import org.eclipse.jetty.servlet.ServletHolder;
  48. import org.eclipse.jetty.servlet.DefaultServlet;
  49. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
  50. import org.springframework.core.io.support.ResourcePatternResolver;
  51. import org.springframework.context.ApplicationContext;
  52. import org.springframework.web.servlet.DispatcherServlet;
  53. import it.tidalwave.messagebus.annotation.ListensTo;
  54. import it.tidalwave.messagebus.annotation.SimpleMessageSubscriber;
  55. import it.tidalwave.bluemarine2.message.PowerOnNotification;
  56. import it.tidalwave.bluemarine2.message.PowerOffNotification;
  57. import it.tidalwave.bluemarine2.rest.spi.ResourceServer;
  58. import lombok.extern.slf4j.Slf4j;
  59. import static it.tidalwave.util.FunctionalCheckedExceptionWrappers.*;

  60. /***********************************************************************************************************************
  61.  *
  62.  * @author  Fabrizio Giudici
  63.  *
  64.  **********************************************************************************************************************/
  65. @SimpleMessageSubscriber @Slf4j
  66. public class DefaultResourceServer implements ResourceServer
  67.   {
  68.     private String ipAddress = "";

  69.     private int port;

  70.     private Server server;

  71.     @Inject
  72.     private ApplicationContext applicationContext;

  73.     /*******************************************************************************************************************
  74.      *
  75.      * {@inheritDoc}
  76.      *
  77.      ******************************************************************************************************************/
  78.     @Override @Nonnull
  79.     public String absoluteUrl (@Nonnull final String type)
  80.       {
  81.         return String.format("http://%s:%d/%s", ipAddress, port, type);
  82.       }

  83.     /*******************************************************************************************************************
  84.      *
  85.      *
  86.      ******************************************************************************************************************/
  87.     @VisibleForTesting
  88.     public void onPowerOnNotification (@ListensTo @Nonnull final PowerOnNotification notification)
  89.       throws Exception
  90.       {
  91.         log.info("onPowerOnNotification({})", notification);
  92.         ipAddress = getNonLoopbackIPv4Address().getHostAddress();
  93.         server = new Server(InetSocketAddress.createUnresolved(ipAddress, Integer.getInteger("port", 0)));

  94.         final ServletContextHandler servletContext = new ServletContextHandler();
  95.         servletContext.setBaseResource(new ResourceCollection(findWebResources()));
  96.         log.info("RESOURCE BASE: {}", servletContext.getResourceBase());
  97.         servletContext.setContextPath("/");
  98.         servletContext.setWelcomeFiles(new String[] { "index.xhtml" });
  99.         final DelegateWebApplicationContext wac = new DelegateWebApplicationContext(applicationContext, servletContext.getServletContext());
  100.         servletContext.addServlet(new ServletHolder("spring", new DispatcherServlet(wac)), "/rest/*");
  101.         servletContext.addServlet(new ServletHolder("default", new DefaultServlet()), "/*");
  102.         servletContext.addFilter(new FilterHolder(new LoggingFilter()), "/*", EnumSet.allOf(DispatcherType.class));
  103.         server.setHandler(servletContext);

  104.         server.start();
  105.         port = ((ServerConnector)server.getConnectors()[0]).getLocalPort();
  106.         log.info(">>>> resource server jetty started at {}:{} ", ipAddress, port);
  107.       }

  108.     /*******************************************************************************************************************
  109.      *
  110.      *
  111.      ******************************************************************************************************************/
  112.     @VisibleForTesting public void onPowerOffNotification (@ListensTo @Nonnull final PowerOffNotification notification)
  113.       throws Exception
  114.       {
  115.         log.info("onPowerOffNotification({})", notification);
  116.         server.stop();
  117.         server.destroy();
  118.       }

  119.     /*******************************************************************************************************************
  120.      *
  121.      *
  122.      *
  123.      ******************************************************************************************************************/
  124.     @Nonnull
  125.     private InetAddress getNonLoopbackIPv4Address()
  126.       throws SocketException
  127.       {
  128.         for (final Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements() ; )
  129.           {
  130.             final NetworkInterface itf =  en.nextElement();

  131.             if (!itf.getName().startsWith("docker"))
  132.               {
  133.                 for (final Enumeration<InetAddress> ee = itf.getInetAddresses(); ee.hasMoreElements() ;)
  134.                   {
  135.                     final InetAddress address = ee.nextElement();

  136.                     if (!address.isLoopbackAddress() && (address instanceof Inet4Address))
  137.                       {
  138.                         return address;
  139.                       }
  140.                   }
  141.               }
  142.           }

  143.         log.warn("Returning loopback address!");
  144.         return InetAddress.getLoopbackAddress();
  145.       }

  146.     /*******************************************************************************************************************
  147.      *
  148.      *
  149.      ******************************************************************************************************************/
  150.     @Nonnull
  151.     private Resource[] findWebResources()
  152.       throws IOException
  153.       {
  154.         final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
  155.         final ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(classLoader);
  156.         return Stream.of(resolver.getResources("classpath*:/webapp"))
  157.                      .map(_f(x -> Resource.newResource(x.getURI())))
  158.                      .toArray(Resource[]::new);
  159.       }
  160.   }