Package com.google.caja.service

Source Code of com.google.caja.service.CajolingService

// Copyright (C) 2008 Google Inc.
//
// 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.google.caja.service;

import com.google.caja.SomethingWidgyHappenedError;
import com.google.caja.lexer.ExternalReference;
import com.google.caja.lexer.FetchedData;
import com.google.caja.lexer.FilePosition;
import com.google.caja.lexer.InputSource;
import com.google.caja.plugin.DataUriFetcher;
import com.google.caja.plugin.UriFetcher;
import com.google.caja.plugin.UriFetcher.ChainingUriFetcher;
import com.google.caja.reporting.BuildInfo;
import com.google.caja.reporting.Message;
import com.google.caja.reporting.MessageContext;
import com.google.caja.reporting.MessagePart;
import com.google.caja.reporting.MessageQueue;
import com.google.caja.util.Charsets;
import com.google.caja.util.ContentType;
import com.google.caja.util.Lists;
import com.google.caja.util.Pair;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Vector;

/**
* A cajoling service which proxies connections:<ul>
*   <li> cajole any javascript
*   <li> cajoles any gadgets
*   <li> checks requested and retrieved mime-types
* </ul>
*
* @author jasvir@gmail.com (Jasvir Nagra)
*/
public class CajolingService {
  static final String DEFAULT_HOST = "http://caja.appspot.com/cajole";

  private final List<ContentHandler> handlers = new Vector<ContentHandler>();
  private final ContentTypeCheck typeCheck = new LooseContentTypeCheck();
  private final String host;
  private final UriFetcher uriFetcher;

  public CajolingService() { this(BuildInfo.getInstance()); }

  public CajolingService(BuildInfo buildInfo) { this(buildInfo, null); }

  public CajolingService(BuildInfo buildInfo, String host) {
    this(buildInfo, host, ChainingUriFetcher.make(
        new DataUriFetcher(),
        new UriFetcher() {
          public FetchedData fetch(ExternalReference ref, String mimeType)
              throws UriFetchException {
            try {
              return FetchedData.fromConnection(
                  ref.getUri().toURL().openConnection());
            } catch (IOException ex) {
              throw new UriFetchException(ref, mimeType, ex);
            }
          }
        }));
  }

  public CajolingService(BuildInfo buildInfo, String host, UriFetcher fetcher) {
    this.host = host != null ? host : DEFAULT_HOST;
    this.uriFetcher = fetcher;
    registerHandlers(buildInfo);
  }

  private boolean emptyOrNull(String str) {
    return null == str || "".equals(str);
  }

  /**
   * Main entry point for the cajoling service.
   *
   * @param inputFetchedData the input content. If this is {@code null}, the
   *     service will attempt to fetch the content from the location given by
   *     the {@link CajaArguments#URL} parameter.
   * @param args a set of arguments to the cajoling service.
   * @param mq a message queue into which status and error messages will be
   *     placed. The caller should query for the most severe status of the
   *     messages in this queue to determine the overall success of the
   *     invocation.
   * @return the output content, or {@code null} if a serious error occurred
   *     that prevented the content from being generated.
   */
  public FetchedData handle(FetchedData inputFetchedData,
                            ContentHandlerArgs args,
                            MessageQueue mq) {
    FetchedData result = doHandle(inputFetchedData, args, mq);
    if (result == null) {
      ByteArrayOutputStream intermediateResponse = new ByteArrayOutputStream();
      Pair<ContentType, String> contentParams =
          AbstractCajolingHandler.getReturnedContentParams(args);
      OutputStreamWriter writer = new OutputStreamWriter(
          intermediateResponse, Charsets.UTF_8);
      try {
        AbstractCajolingHandler.renderAsJSON(
            (String)null, (String)null, contentParams.b, mq, writer, false);
      } catch (IOException e) {
        // Unlikely IOException to byte array; rethrow
        throw new SomethingWidgyHappenedError(e);
      }
      result = FetchedData.fromBytes(
          intermediateResponse.toByteArray(),
          ContentType.JSON.mimeType,
          "UTF-8",
          InputSource.UNKNOWN);
    }
    return result;
  }

  public FetchedData doHandle(FetchedData inputFetchedData,
                              ContentHandlerArgs args,
                              MessageQueue mq) {
    String buildVersion = CajaArguments.BUILD_VERSION.get(args);
    if (buildVersion != null) {
      boolean versionMatch =
          BuildInfo.getInstance().getBuildVersion().equals(buildVersion);
      if (!versionMatch) {
        mq.addMessage(
            ServiceMessageType.WRONG_BUILD_VERSION,
            MessagePart.Factory.valueOf(
                BuildInfo.getInstance().getBuildVersion()),
            MessagePart.Factory.valueOf(
                CajaArguments.BUILD_VERSION.get(args)));
        return null;
      }
    }

    String inputUrlString = CajaArguments.URL.get(args);
    URI inputUri;
    if (inputUrlString == null && inputFetchedData == null &&
        emptyOrNull(CajaArguments.CONTENT.get(args))) {
      mq.addMessage(
          ServiceMessageType.MISSING_ARGUMENT,
          MessagePart.Factory.valueOf(CajaArguments.URL.toString()));
      return null;
    } else if (inputUrlString == null) {
      inputUri = InputSource.UNKNOWN.getUri();
    } else {
      try {
        inputUri = new URI(inputUrlString);
      } catch (URISyntaxException ex) {
        mq.addMessage(
            ServiceMessageType.INVALID_INPUT_URL,
            MessagePart.Factory.valueOf(inputUrlString));
        return null;
      }
    }

    String expectedInputContentType = CajaArguments.INPUT_MIME_TYPE.get(args);
    if (expectedInputContentType == null) {
      mq.addMessage(
          ServiceMessageType.MISSING_ARGUMENT,
          MessagePart.Factory.valueOf(
              CajaArguments.INPUT_MIME_TYPE.toString()));
      return null;
    }

    if (inputFetchedData == null) {
      String content = CajaArguments.CONTENT.get(args);
      if (emptyOrNull(content)) {
        try {
          inputFetchedData = uriFetcher.fetch(
              new ExternalReference(inputUri, FilePosition.UNKNOWN),
              expectedInputContentType);
        } catch (UriFetcher.UriFetchException ex) {
          ex.toMessageQueue(mq);
          return null;
        }
      } else {
        try {
          inputFetchedData = FetchedData.fromReader(new StringReader(content),
              new InputSource(inputUri), expectedInputContentType,
              Charsets.UTF_8.displayName());
        } catch (IOException e) {
          return null;
        }
      }
    }

    if (!typeCheck.check(
            expectedInputContentType,
            inputFetchedData.getContentType())) {
      mq.addMessage(
          ServiceMessageType.UNEXPECTED_INPUT_MIME_TYPE,
          MessagePart.Factory.valueOf(expectedInputContentType),
          MessagePart.Factory.valueOf(inputFetchedData.getContentType()));
      return null;
    }

    String transformName = CajaArguments.TRANSFORM.get(args);
    Transform transform = null;
    if (transformName != null) {
      try {
        transform = Transform.valueOf(transformName);
      } catch (Exception e) {
        mq.addMessage(
            ServiceMessageType.INVALID_ARGUMENT,
            MessagePart.Factory.valueOf(transformName),
            MessagePart.Factory.valueOf(CajaArguments.TRANSFORM.toString()));
        return null;
      }
    }

    // TODO(jasvir): Change CajaArguments to handle >1 occurrence of arg
    String directiveName = CajaArguments.DIRECTIVE.get(args);
    List<Directive> directive = Lists.newArrayList();
    if (directiveName != null) {
      try {
        directive.add(Directive.valueOf(directiveName));
      } catch (Exception e) {
        mq.addMessage(
            ServiceMessageType.INVALID_ARGUMENT,
            MessagePart.Factory.valueOf(directiveName),
            MessagePart.Factory.valueOf(CajaArguments.DIRECTIVE.toString()));
        return null;
      }
    }

    ByteArrayOutputStream intermediateResponse = new ByteArrayOutputStream();
    Pair<String, String> contentInfo;
    try {
      contentInfo = applyHandler(
          inputUri,
          transform,
          directive,
          args,
          inputFetchedData.getContentType(),
          inputFetchedData,
          intermediateResponse,
          mq);
    } catch (UnsupportedContentTypeException e) {
      mq.addMessage(ServiceMessageType.UNSUPPORTED_CONTENT_TYPES);
      return null;
    } catch (RuntimeException e) {
      mq.addMessage(
          ServiceMessageType.EXCEPTION_IN_SERVICE,
          MessagePart.Factory.valueOf(e.toString()));
      return null;
    }

    return FetchedData.fromBytes(
        intermediateResponse.toByteArray(),
        contentInfo.a,
        contentInfo.b,
        new InputSource(inputUri));
  }

  public static String render(MessageQueue mq) {
    StringBuilder sb = new StringBuilder();
    for (Message m : mq.getMessages()) {
      try {
        m.format(new MessageContext(), sb);
      } catch (IOException e) {
        sb.append(e.toString());
      }
      sb.append("\n");
    }
    return sb.toString();
  }

  private void registerHandlers(BuildInfo buildInfo) {
    handlers.add(new JsHandler(buildInfo));
    handlers.add(new HtmlHandler(buildInfo, host, uriFetcher));
    handlers.add(new ProxyHandler(buildInfo, uriFetcher));
  }

  private Pair<String, String> applyHandler(
      URI uri, Transform t, List<Directive> d, ContentHandlerArgs args,
      String inputContentType,
      FetchedData input, OutputStream response, MessageQueue mq)
      throws UnsupportedContentTypeException {
    for (ContentHandler handler : handlers) {
      if (handler.canHandle(uri, t, d, inputContentType, typeCheck)) {
        return handler.apply(uri, t, d, args, inputContentType,
            typeCheck, input, response, mq);
      }
    }
    throw new UnsupportedContentTypeException();
  }

  public static enum Directive {
    STRICT,
    ES53;
  }

  public static enum Transform {
    PROXY,
    CAJOLE;
  }

  public static final String RENDER_PRETTY = "pretty";
}
TOP

Related Classes of com.google.caja.service.CajolingService

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.