Package org.apache.wicket.resource.bundles

Source Code of org.apache.wicket.resource.bundles.ConcatBundleResource

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.wicket.resource.bundles;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.MissingResourceException;

import javax.servlet.http.HttpServletResponse;

import org.apache.wicket.Application;
import org.apache.wicket.markup.head.IReferenceHeaderItem;
import org.apache.wicket.request.resource.AbstractResource;
import org.apache.wicket.request.resource.IResource;
import org.apache.wicket.request.resource.ResourceReference;
import org.apache.wicket.request.resource.caching.IStaticCacheableResource;
import org.apache.wicket.resource.ITextResourceCompressor;
import org.apache.wicket.util.io.ByteArrayOutputStream;
import org.apache.wicket.util.io.IOUtils;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.lang.Bytes;
import org.apache.wicket.util.lang.Classes;
import org.apache.wicket.util.resource.AbstractResourceStream;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
import org.apache.wicket.util.time.Time;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A {@linkplain IResource resource} that concatenates several resources into one download. This
* resource can only bundle {@link IStaticCacheableResource}s. The content type of the resource will
* be that of the first resource that specifies its content type.
*
* @author papegaaij
*/
public class ConcatBundleResource extends AbstractResource implements IStaticCacheableResource
{
  private static final Logger log = LoggerFactory.getLogger(ConcatBundleResource.class);

  private static final long serialVersionUID = 1L;

  private final List<? extends IReferenceHeaderItem> providedResources;

  private boolean cachingEnabled;

  /**
   * An optional compressor that will be used to compress the bundle resources
   */
  private ITextResourceCompressor compressor;

  /**
   * Construct.
   *
   * @param providedResources
   */
  public ConcatBundleResource(List<? extends IReferenceHeaderItem> providedResources)
  {
    this.providedResources = Args.notNull(providedResources, "providedResources");
    cachingEnabled = true;
  }

  @Override
  protected ResourceResponse newResourceResponse(Attributes attributes)
  {
    final ResourceResponse resourceResponse = new ResourceResponse();

    if (resourceResponse.dataNeedsToBeWritten(attributes))
    {
      try
      {
        List<IResourceStream> resources = collectResourceStreams();
        if (resources == null)
          return sendResourceError(resourceResponse, HttpServletResponse.SC_NOT_FOUND,
            "Unable to find resource");

        resourceResponse.setContentType(findContentType(resources));

        // add Last-Modified header (to support HEAD requests and If-Modified-Since)
        final Time lastModified = findLastModified(resources);

        if (lastModified != null)
          resourceResponse.setLastModified(lastModified);

        // read resource data
        final byte[] bytes = readAllResources(resources);

        // send Content-Length header
        resourceResponse.setContentLength(bytes.length);

        // send response body with resource data
        resourceResponse.setWriteCallback(new WriteCallback()
        {
          @Override
          public void writeData(Attributes attributes)
          {
            attributes.getResponse().write(bytes);
          }
        });
      }
      catch (IOException e)
      {
        log.debug(e.getMessage(), e);
        return sendResourceError(resourceResponse, 500, "Unable to read resource stream");
      }
      catch (ResourceStreamNotFoundException e)
      {
        log.debug(e.getMessage(), e);
        return sendResourceError(resourceResponse, 500, "Unable to open resource stream");
      }
    }

    return resourceResponse;
  }

  private List<IResourceStream> collectResourceStreams()
  {
    List<IResourceStream> ret = new ArrayList<>(providedResources.size());
    for (IReferenceHeaderItem curItem : providedResources)
    {
      IResourceStream stream = ((IStaticCacheableResource)curItem.getReference()
        .getResource()).getCacheableResourceStream();
      if (stream == null)
      {
        reportError(curItem.getReference(), "Cannot get resource stream for ");
        return null;
      }

      ret.add(stream);
    }
    return ret;
  }

  protected String findContentType(List<IResourceStream> resources)
  {
    for (IResourceStream curStream : resources)
      if (curStream.getContentType() != null)
        return curStream.getContentType();
    return null;
  }

  protected Time findLastModified(List<IResourceStream> resources)
  {
    Time ret = null;
    for (IResourceStream curStream : resources)
    {
      Time curLastModified = curStream.lastModifiedTime();
      if (ret == null || curLastModified.after(ret))
        ret = curLastModified;
    }
    return ret;
  }

  protected byte[] readAllResources(List<IResourceStream> resources) throws IOException,
    ResourceStreamNotFoundException
  {
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    for (IResourceStream curStream : resources)
      IOUtils.copy(curStream.getInputStream(), output);

    byte[] bytes = output.toByteArray();

    if (getCompressor() != null)
    {
      String nonCompressed = new String(bytes, "UTF-8");
      bytes = getCompressor().compress(nonCompressed).getBytes("UTF-8");
    }

    return bytes;
  }

  private ResourceResponse sendResourceError(ResourceResponse resourceResponse, int errorCode,
    String errorMessage)
  {
    if (log.isWarnEnabled())
    {
      String msg = String.format("Bundled resource: %s (status=%d)", errorMessage, errorCode);
      log.warn(msg);
    }

    resourceResponse.setError(errorCode, errorMessage);
    return resourceResponse;
  }

  @Override
  public boolean isCachingEnabled()
  {
    return cachingEnabled;
  }

  public void setCachingEnabled(final boolean enabled)
  {
    cachingEnabled = enabled;
  }

  @Override
  public Serializable getCacheKey()
  {
    ArrayList<Serializable> key = new ArrayList<>(providedResources.size());
    for (IReferenceHeaderItem curItem : providedResources)
    {
      Serializable curKey = ((IStaticCacheableResource)curItem.getReference().getResource()).getCacheKey();
      if (curKey == null)
      {
        reportError(curItem.getReference(), "Unable to get cache key for ");
        return null;
      }
      key.add(curKey);
    }
    return key;
  }

  /**
   * If a bundle resource is missing then throws a {@link MissingResourceException} if
   * {@link org.apache.wicket.settings.IResourceSettings#getThrowExceptionOnMissingResource()}
   * says so, or logs a warning message if the logging level allows
   * @param reference
   *              The resource reference to the missing resource
   * @param prefix
   *              The error message prefix
   */
  private void reportError(ResourceReference reference, String prefix)
  {
    String scope = Classes.name(reference.getScope());
    String name = reference.getName();
    String message = prefix + reference.toString();

    if (getThrowExceptionOnMissingResource())
    {
      throw new MissingResourceException(message, scope, name);
    }
    else if (log.isWarnEnabled())
    {
      log.warn(message);
    }
  }

  @Override
  public IResourceStream getCacheableResourceStream()
  {
    List<IResourceStream> resources = collectResourceStreams();

    if (resources == null)
    {
      return null;
    }

    byte[] bytes;
    try
    {
      bytes = readAllResources(resources);
    }
    catch (IOException e)
    {
      return null;
    }
    catch (ResourceStreamNotFoundException e)
    {
      return null;
    }

    final String contentType = findContentType(resources);
    final Time lastModified = findLastModified(resources);
    final ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
    final long length = bytes.length;
    AbstractResourceStream ret = new AbstractResourceStream()
    {
      private static final long serialVersionUID = 1L;

      @Override
      public InputStream getInputStream() throws ResourceStreamNotFoundException
      {
        return inputStream;
      }

      @Override
      public Bytes length()
      {
        return Bytes.bytes(length);
      }

      @Override
      public String getContentType()
      {
        return contentType;
      }

      @Override
      public Time lastModifiedTime()
      {
        return lastModified;
      }

      @Override
      public void close() throws IOException
      {
        inputStream.close();
      }
    };
    return ret;
  }

  public void setCompressor(ITextResourceCompressor compressor)
  {
    this.compressor = compressor;
  }

  public ITextResourceCompressor getCompressor()
  {
    return compressor;
  }

  /**
   * @return the result of {@link org.apache.wicket.settings.IResourceSettings#getThrowExceptionOnMissingResource()}
   */
  protected boolean getThrowExceptionOnMissingResource()
  {
    return Application.get().getResourceSettings().getThrowExceptionOnMissingResource();
  }
}
TOP

Related Classes of org.apache.wicket.resource.bundles.ConcatBundleResource

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.