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