Package org.sonatype.nexus.extdirect.internal

Source Code of org.sonatype.nexus.extdirect.internal.ExtDirectServlet$RequestBoundReader

/*
* Sonatype Nexus (TM) Open Source Version
* Copyright (c) 2007-2014 Sonatype, Inc.
* All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
*
* This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
* which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
* of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
* Eclipse Foundation. All other trademarks are the property of their respective owners.
*/

package org.sonatype.nexus.extdirect.internal;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Writer;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.servlet.ServletConfig;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.sonatype.configuration.validation.InvalidConfigurationException;
import org.sonatype.configuration.validation.ValidationResponse;
import org.sonatype.nexus.analytics.EventDataBuilder;
import org.sonatype.nexus.analytics.EventRecorder;
import org.sonatype.nexus.configuration.application.ApplicationDirectories;
import org.sonatype.nexus.extdirect.DirectComponent;
import org.sonatype.nexus.extdirect.ExtDirectPlugin;
import org.sonatype.nexus.extdirect.model.Response;
import org.sonatype.security.SecuritySystem;

import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Maps.EntryTransformer;
import com.google.inject.Key;
import com.softwarementors.extjs.djn.EncodingUtils;
import com.softwarementors.extjs.djn.api.RegisteredMethod;
import com.softwarementors.extjs.djn.api.Registry;
import com.softwarementors.extjs.djn.config.ApiConfiguration;
import com.softwarementors.extjs.djn.config.GlobalConfiguration;
import com.softwarementors.extjs.djn.router.RequestRouter;
import com.softwarementors.extjs.djn.router.dispatcher.Dispatcher;
import com.softwarementors.extjs.djn.router.processor.poll.PollRequestProcessor;
import com.softwarementors.extjs.djn.servlet.DirectJNgineServlet;
import com.softwarementors.extjs.djn.servlet.ssm.SsmDispatcher;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.eclipse.sisu.BeanEntry;
import org.eclipse.sisu.inject.BeanLocator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

import static com.google.common.base.Preconditions.checkNotNull;
import static org.sonatype.nexus.extdirect.model.Responses.error;
import static org.sonatype.nexus.extdirect.model.Responses.invalid;
import static org.sonatype.nexus.extdirect.model.Responses.success;

/**
* Ext.Direct Servlet.
*
* @since 3.0
*/
@Named
@Singleton
public class ExtDirectServlet
    extends DirectJNgineServlet
{

  private static final Logger log = LoggerFactory.getLogger(ExtDirectServlet.class);

  private final ApplicationDirectories directories;

  private final BeanLocator beanLocator;

  private final SecuritySystem securitySystem;

  private final EventRecorder eventRecorder;

  @Inject
  public ExtDirectServlet(final ApplicationDirectories directories,
                          final BeanLocator beanLocator,
                          final SecuritySystem securitySystem,
                          final @Nullable EventRecorder eventRecorder)
  {
    this.directories = checkNotNull(directories);
    this.beanLocator = checkNotNull(beanLocator);
    this.securitySystem = checkNotNull(securitySystem);
    this.eventRecorder = eventRecorder; // null okay
  }

  @Override
  public void doPost(final HttpServletRequest request, final HttpServletResponse response) throws IOException {
    HttpServletRequest wrappedRequest = new HttpServletRequestWrapper(request)
    {
      private BufferedReader reader;

      @Override
      public BufferedReader getReader() throws IOException {
        if (reader == null) {
          try {
            reader = super.getReader();
          }
          catch (IllegalStateException e) {
            // HACK avoid "java.lang.IllegalStateException: STREAMED" which is thrown in case of request.getReader()
            // and request.getInputStream() was already called as in case of getting request parameters
            reader = new RequestBoundReader(new InputStreamReader(getInputStream(), EncodingUtils.UTF8), request);
          }
        }
        return reader;
      }
    };
    super.doPost(wrappedRequest, response);
  }

  @Override
  protected List<ApiConfiguration> createApiConfigurationsFromServletConfigurationApi(
      final ServletConfig configuration)
  {
    Iterable<? extends BeanEntry<Annotation, DirectComponent>> entries =
        beanLocator.locate(Key.get(DirectComponent.class));

    List<Class<?>> apiClasses = Lists.newArrayList(
        Iterables.transform(entries, new Function<BeanEntry<Annotation, DirectComponent>, Class<?>>()
        {
          @Nullable
          @Override
          public Class<?> apply(final BeanEntry<Annotation, DirectComponent> input) {
            Class<DirectComponent> implementationClass = input.getImplementationClass();
            log.debug("Registering Ext.Direct component '{}'", implementationClass);
            return implementationClass;
          }
        })
    );
    File apiFile = new File(directories.getTemporaryDirectory(), ExtDirectPlugin.ARTIFACT_ID + "/api.js");
    return Lists.newArrayList(
        new ApiConfiguration(
            "nexus",
            apiFile.getName(),
            apiFile.getAbsolutePath(),
            "NX.direct.api",
            "NX.direct",
            apiClasses
        )
    );
  }

  @Override
  protected Dispatcher createDispatcher(final Class<? extends Dispatcher> cls) {
    return new SsmDispatcher()
    {
      @Override
      protected Object createInvokeInstanceForMethodWithDefaultConstructor(final RegisteredMethod method)
          throws Exception
      {
        if (log.isDebugEnabled()) {
          log.debug(
              "Creating instance of action class '{}' mapped to '{}",
              method.getActionClass().getName(), method.getActionName()
          );
        }

        @SuppressWarnings("unchecked")
        Iterable<BeanEntry<Annotation, Object>> actionInstance = beanLocator.locate(
            Key.get((Class) method.getActionClass())
        );
        return actionInstance.iterator().next().getValue();
      }

      @Override
      protected Object invokeMethod(final RegisteredMethod method, final Object actionInstance,
                                    final Object[] parameters) throws Exception
      {
        if (log.isDebugEnabled()) {
          log.debug("Invoking action method: {}, java-method: {}", method.getFullName(),
              method.getFullJavaMethodName());
        }

        Subject subject = securitySystem.getSubject();
        if ((subject == null || !subject.isAuthenticated()) && securitySystem.isAnonymousAccessEnabled()) {
          try {
            securitySystem.login(new UsernamePasswordToken(
                securitySystem.getAnonymousUsername(), securitySystem.getAnonymousPassword()
            ));
          }
          catch (Exception e) {
            log.error("Could not log in anonymous user");
          }
        }

        Response response = null;
        EventDataBuilder builder = null;

        // Maybe record analytics events
        if (eventRecorder != null && eventRecorder.isEnabled()) {
          builder = new EventDataBuilder("Ext.Direct")
              .set("type", method.getType().name())
              .set("name", method.getName())
              .set("action", method.getActionName());
        }

        MDC.put(getClass().getName(), method.getFullName());

        try {
          response = asResponse(super.invokeMethod(method, actionInstance, parameters));
        }
        catch (InvocationTargetException e) {
          response = handleException(method, e.getTargetException());
        }
        catch (Throwable e) {
          response = handleException(method, e);
        }
        finally {
          // Record analytics event
          if (builder != null) {
            if (response != null) {
              builder.set("success", response.isSuccess());
            }
            eventRecorder.record(builder.build());
          }

          MDC.remove(getClass().getName());
        }

        return response;
      }

      private Response handleException(final RegisteredMethod method, final Throwable e) {
        log.debug("Failed to invoke action method: {}, java-method: {}", method.getFullName(),
            method.getFullJavaMethodName(), e);

        if (e instanceof InvalidConfigurationException) {
          InvalidConfigurationException cause = (InvalidConfigurationException) e;
          ValidationResponse vr = cause.getValidationResponse();
          if (vr == null || vr.getValidationErrors() == null || vr.getValidationErrors().size() == 0) {
            return asResponse(error(e));
          }
          return asResponse(invalid(cause));
        }

        if (e instanceof ConstraintViolationException) {
          ConstraintViolationException cause = (ConstraintViolationException) e;
          Set<ConstraintViolation<?>> violations = cause.getConstraintViolations();
          if (violations == null || violations.size() == 0) {
            return asResponse(error(e));
          }
          return asResponse(invalid(cause));
        }

        return asResponse(error(e));
      }

      private Response asResponse(final Object result) {
        Response response;
        if (result == null) {
          response = success();
        }
        else {
          if (result instanceof Response) {
            response = (Response) result;
          }
          else {
            response = success(result);
          }
        }
        return response;
      }
    };
  }

  @Override
  protected RequestRouter createRequestRouter(final Registry registry, final GlobalConfiguration globalConfiguration) {
    final Dispatcher dispatcher = createDispatcher(globalConfiguration.getDispatcherClass());
    return new RequestRouter(registry, globalConfiguration, dispatcher)
    {
      @Override
      public void processPollRequest(final Reader reader, final Writer writer, final String pathInfo)
          throws IOException
      {
        new PollRequestProcessor(registry, dispatcher, globalConfiguration)
        {
          @Override
          // HACK: we determine parameters from request not by reading request content as request content could had
          // been already read exactly for getting the params, case when request content is already empty
          protected Object[] getParameters()
          {
            if (reader instanceof RequestBoundReader) {
              ServletRequest request = ((RequestBoundReader) reader).getRequest();
              Map<String, String[]> parameterMap = request.getParameterMap();
              Map<String, String> parameters = Maps.newHashMap();
              if (parameterMap != null) {
                parameters = Maps.transformEntries(parameterMap, new EntryTransformer<String, String[], String>()
                {
                  @Override
                  public String transformEntry(@Nullable final String key, @Nullable final String[] values) {
                    return values == null || values.length == 0 ? null : values[0];
                  }
                });
              }
              return new Object[]{parameters};
            }
            return super.getParameters();
          }
        }.process(reader, writer, pathInfo);
      }
    };
  }

  private static class RequestBoundReader
      extends BufferedReader
  {
    private final ServletRequest request;

    public RequestBoundReader(final Reader in, final ServletRequest request) {
      super(in);
      this.request = request;
    }

    private ServletRequest getRequest() {
      return request;
    }
  }
}
TOP

Related Classes of org.sonatype.nexus.extdirect.internal.ExtDirectServlet$RequestBoundReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.