All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.helger.photon.api.servlet.APIXServletHandler Maven / Gradle / Ivy

There is a newer version: 8.2.4
Show newest version
/**
 * Copyright (C) 2014-2020 Philip Helger (www.helger.com)
 * philip[at]helger[dot]com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.helger.photon.api.servlet;

import java.io.IOException;

import javax.annotation.Nonnull;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.helger.commons.ValueEnforcer;
import com.helger.commons.functional.ISupplier;
import com.helger.commons.http.CHttp;
import com.helger.commons.http.EHttpMethod;
import com.helger.commons.lang.GenericReflection;
import com.helger.commons.mutable.MutableInt;
import com.helger.http.EHttpVersion;
import com.helger.photon.api.APIPath;
import com.helger.photon.api.GlobalAPIInvoker;
import com.helger.photon.api.IAPIInvoker;
import com.helger.photon.api.IAPIRegistry;
import com.helger.photon.api.InvokableAPIDescriptor;
import com.helger.photon.app.PhotonUnifiedResponse;
import com.helger.servlet.response.UnifiedResponse;
import com.helger.web.scope.IRequestWebScope;
import com.helger.web.scope.IRequestWebScopeWithoutResponse;
import com.helger.xservlet.handler.simple.IXServletSimpleHandler;

/**
 * Abstract API servlet. Use {@link IAPIRegistry} to register API functions
 * dynamically and {@link IAPIInvoker} to invoke them afterwards.
 *
 * @author Philip Helger
 */
public class APIXServletHandler implements IXServletSimpleHandler
{
  private static final Logger LOGGER = LoggerFactory.getLogger (APIXServletHandler.class);

  private final ISupplier  m_aRegistryFactory;
  private final ISupplier  m_aInvokerFactory;

  public APIXServletHandler ()
  {
    this ( () -> GlobalAPIInvoker.getInstance ().getRegistry (), () -> GlobalAPIInvoker.getInstance ().getInvoker ());
  }

  public APIXServletHandler (@Nonnull final ISupplier  aRegistryFactory,
                             @Nonnull final ISupplier  aInvokerFactory)
  {
    m_aRegistryFactory = ValueEnforcer.notNull (aRegistryFactory, "RegistryFactory");
    m_aInvokerFactory = ValueEnforcer.notNull (aInvokerFactory, "InvokerFactory");
  }

  @Nonnull
  public PhotonUnifiedResponse createUnifiedResponse (@Nonnull final EHttpVersion eHttpVersion,
                                                      @Nonnull final EHttpMethod eHttpMethod,
                                                      @Nonnull final HttpServletRequest aHttpRequest,
                                                      @Nonnull final IRequestWebScope aRequestScope)
  {
    return new PhotonUnifiedResponse (eHttpVersion, eHttpMethod, aHttpRequest, aRequestScope);
  }

  @Override
  public void handleRequest (@Nonnull final IRequestWebScopeWithoutResponse aRequestScope,
                             @Nonnull final UnifiedResponse aUnifiedResponse) throws Exception
  {
    final EHttpMethod eHttpMethod = aRequestScope.getHttpMethod ();
    if (eHttpMethod == null)
    {
      // Should have been caught in AbstractXServlet.service ...
      LOGGER.error ("Missing HTTP method in request: " + aRequestScope.getRequest ());

      // No such action
      aUnifiedResponse.setStatus (CHttp.HTTP_NOT_IMPLEMENTED);
      return;
    }

    final APIPath aAPIPath = APIPath.createForServlet (aRequestScope);
    final IAPIRegistry aRegistry = m_aRegistryFactory.get ();
    final IAPIInvoker aInvoker = m_aInvokerFactory.get ();

    final InvokableAPIDescriptor aInvokableDescriptor = aRegistry.getAPIByPath (aAPIPath);
    if (aInvokableDescriptor == null)
    {
      LOGGER.warn ("Unknown API " + eHttpMethod + " '" + aAPIPath.getPath () + "' requested!");

      // No such action
      aUnifiedResponse.setStatus (CHttp.HTTP_NOT_FOUND);
    }
    else
    {
      // Is the declaration applicable for the current scope?
      // Check for required headers and parameters
      final MutableInt aStatusCode = new MutableInt (CHttp.HTTP_BAD_REQUEST);
      if (!aInvokableDescriptor.canExecute (aRequestScope, aStatusCode))
      {
        final int nStatusCode = aStatusCode.intValue ();
        LOGGER.warn ("API " +
                     eHttpMethod +
                     " '" +
                     aAPIPath.getPath () +
                     "' cannot be executed for the current request. Returning HTTP " +
                     nStatusCode);
        aUnifiedResponse.setStatus (nStatusCode);
      }
      else
      {
        // Don't cache the result. The executor may overwrite it
        aUnifiedResponse.disableCaching ();

        try
        {
          // Main API invocation
          aInvoker.invoke (aInvokableDescriptor, aRequestScope, GenericReflection.uncheckedCast (aUnifiedResponse));

          if (aUnifiedResponse.isStatusCodeDefined () || aUnifiedResponse.isRedirectDefined ())
          {
            // Status codes are not meant to be cached
            aUnifiedResponse.removeCaching ();
          }
          else
            if (!aUnifiedResponse.isStatusCodeDefined () &&
                !aUnifiedResponse.isRedirectDefined () &&
                !aUnifiedResponse.hasContent ())
            {
              // Set "No Content" response
              aUnifiedResponse.setStatus (CHttp.HTTP_NO_CONTENT);
            }
        }
        catch (final IOException | ServletException ex)
        {
          // Re-throw
          throw ex;
        }
        catch (final Throwable t)
        {
          throw new ServletException ("Error invoking API " + eHttpMethod + " '" + aAPIPath.getPath () + "'", t);
        }
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy