001// Copyright 2010-2013 The Apache Software Foundation 002// 003// Licensed under the Apache License, Version 2.0 (the "License"); 004// you may not use this file except in compliance with the License. 005// You may obtain a copy of the License at 006// 007// http://www.apache.org/licenses/LICENSE-2.0 008// 009// Unless required by applicable law or agreed to in writing, software 010// distributed under the License is distributed on an "AS IS" BASIS, 011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012// See the License for the specific language governing permissions and 013// limitations under the License. 014 015package org.apache.tapestry5.internal.services.assets; 016 017import org.apache.tapestry5.internal.services.ResourceStreamer; 018import org.apache.tapestry5.ioc.IOOperation; 019import org.apache.tapestry5.ioc.OperationTracker; 020import org.apache.tapestry5.services.LocalizationSetter; 021import org.apache.tapestry5.services.Request; 022import org.apache.tapestry5.services.Response; 023import org.apache.tapestry5.services.assets.AssetRequestHandler; 024import org.apache.tapestry5.services.assets.StreamableResource; 025import org.apache.tapestry5.services.javascript.JavaScriptStackSource; 026import org.slf4j.Logger; 027 028import java.io.IOException; 029import java.util.regex.Matcher; 030import java.util.regex.Pattern; 031 032public class StackAssetRequestHandler implements AssetRequestHandler 033{ 034 private final Logger logger; 035 036 private final LocalizationSetter localizationSetter; 037 038 private final ResourceStreamer resourceStreamer; 039 040 // Group 1: checksum 041 // Group 2: locale 042 // Group 3: path 043 private final Pattern pathPattern = Pattern.compile("^(.+)/(.+)/(.+)\\.js$"); 044 045 private final OperationTracker tracker; 046 047 private final JavaScriptStackAssembler javaScriptStackAssembler; 048 049 private final JavaScriptStackSource stackSource; 050 051 public StackAssetRequestHandler(Logger logger, LocalizationSetter localizationSetter, 052 ResourceStreamer resourceStreamer, 053 OperationTracker tracker, 054 JavaScriptStackAssembler javaScriptStackAssembler, 055 JavaScriptStackSource stackSource) 056 { 057 this.logger = logger; 058 this.localizationSetter = localizationSetter; 059 this.resourceStreamer = resourceStreamer; 060 this.tracker = tracker; 061 this.javaScriptStackAssembler = javaScriptStackAssembler; 062 this.stackSource = stackSource; 063 } 064 065 public boolean handleAssetRequest(Request request, Response response, final String extraPath) throws IOException 066 { 067 return tracker.perform(String.format("Streaming JavaScript asset stack %s", extraPath), 068 new IOOperation<Boolean>() 069 { 070 public Boolean perform() throws IOException 071 { 072 return streamStackResource(extraPath); 073 } 074 }); 075 } 076 077 private boolean streamStackResource(String extraPath) throws IOException 078 { 079 Matcher matcher = pathPattern.matcher(extraPath); 080 081 if (!matcher.matches()) 082 { 083 logger.warn(String.format("Unable to parse '%s' as an asset stack path", extraPath)); 084 085 return false; 086 } 087 088 String checksum = matcher.group(1); 089 String localeName = matcher.group(2); 090 final String stackName = matcher.group(3); 091 092 final boolean compressed = checksum.startsWith("z"); 093 094 if (compressed) 095 { 096 checksum = checksum.substring(1); 097 } 098 099 if (stackSource.findStack(stackName) == null) 100 { 101 logger.warn(String.format("JavaScript stack '%s' not found.", stackName)); 102 return false; 103 } 104 105 106 // Yes, I have a big regret that the JavaScript stack stuff relies on this global, rather than 107 // having it passed around properly. 108 109 localizationSetter.setNonPersistentLocaleFromLocaleName(localeName); 110 111 StreamableResource resource = 112 tracker.perform(String.format("Assembling JavaScript asset stack '%s' (%s)", 113 stackName, localeName), 114 new IOOperation<StreamableResource>() 115 { 116 public StreamableResource perform() throws IOException 117 { 118 119 return javaScriptStackAssembler.assembleJavaScriptResourceForStack(stackName, compressed); 120 121 } 122 }); 123 124 125 if (resource == null) 126 { 127 return false; 128 } 129 130 return resourceStreamer.streamResource(resource, checksum, ResourceStreamer.DEFAULT_OPTIONS); 131 } 132}