asset.pipeline.fs.JarAssetResolver.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of asset-pipeline-core Show documentation
Show all versions of asset-pipeline-core Show documentation
JVM Asset Pipeline library for serving static web assets, bundling, minifying, and extensibility for transpiling.
/*
* Copyright 2014 the original author or authors.
*
* 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 asset.pipeline.fs
import asset.pipeline.*
import groovy.transform.CompileStatic
import groovy.util.logging.Commons
import java.util.jar.JarEntry
import java.util.regex.Pattern
import java.util.jar.JarFile
import java.util.zip.ZipEntry
import java.util.zip.ZipException
import java.io.BufferedInputStream
/**
* Implementation of the {@link AssetResolver} interface for resolving from JAR files or ZIP files
*
* @author David Estes
* @author Graeme Rocher
*/
@Commons
class JarAssetResolver extends AbstractAssetResolver {
static String QUOTED_FILE_SEPARATOR = Pattern.quote("/")
static String DIRECTIVE_FILE_SEPARATOR = '/'
JarFile baseJar
String prefixPath
JarAssetResolver(String name,String jarPath, String prefixPath) {
super(name)
try {
baseJar = new JarFile(jarPath)
} catch (ZipException x) {
log.error "ERROR: Jar file is corrupted ${jarPath}"
throw x
}
this.prefixPath = prefixPath
}
AssetFile getAsset(String relativePath, String contentType = null, String extension = null, AssetFile baseFile=null) {
if(!relativePath) {
return null
}
if(relativePath.startsWith('/')) {
relativePath = relativePath.substring(1)
}
def normalizedPath = AssetHelper.normalizePath(relativePath)
def specs
if(contentType) {
specs = AssetHelper.getPossibleFileSpecs(contentType)
} else {
if(!extension) {
extension = AssetHelper.extensionFromURI(relativePath)
}
specs = AssetHelper.assetFileClasses().findAll { it.extensions.contains(extension) }
}
AssetFile assetFile = resolveAsset(specs, prefixPath, normalizedPath, baseFile, extension)
return assetFile
}
protected Closure createInputStreamClosure(ZipEntry file) {
if(file) {
return {-> new BufferedInputStream(baseJar.getInputStream(file),512) }
}
return null
}
@CompileStatic
public List getAssets(String basePath, String contentType = null, String extension = null, Boolean recursive = true, AssetFile relativeFile=null, AssetFile baseFile = null) {
def fileList = []
if(!basePath.startsWith('/') && relativeFile != null) {
List pathArgs = relativeFile.parentPath ? relativeFile.parentPath.split(DIRECTIVE_FILE_SEPARATOR).toList() as List : [] as List //(path should be relative not canonical)
String[] basePathArgs = basePath.split(DIRECTIVE_FILE_SEPARATOR)
List parentPathArgs = pathArgs ? pathArgs[0..(pathArgs.size() - 1)] as List : [] as List
parentPathArgs.addAll(basePathArgs.toList() as List)
parentPathArgs = (parentPathArgs).findAll{String it -> it != "."} as List
basePath = parentPathArgs.join(File.separator)
}
def combinedPath = basePath ? [prefixPath, basePath].join("/") : prefixPath
basePath = AssetHelper.normalizePath(combinedPath + "/")
baseJar.entries().each { JarEntry entry ->
if(entry.name.startsWith(basePath)) {
String[] mimeType = AssetHelper.assetMimeTypeForURI(entry.name)
if(!entry.isDirectory() && mimeType && contentType in mimeType) {
fileList << assetForFile(entry,contentType, baseFile, prefixPath)
}
}
}
return fileList
}
@Override
@CompileStatic
protected String getFileName(ZipEntry file) {
return file.name
}
@CompileStatic
protected ZipEntry getRelativeFile(String relativePath, String name) {
return baseJar.getEntry([relativePath, name].join("/"))
}
@CompileStatic
protected String relativePathToResolver(ZipEntry file, String scanDirectoryPath) {
def filePath = file.name
if(filePath.startsWith(scanDirectoryPath)) {
return filePath.substring(scanDirectoryPath.size() + 1).replace(QUOTED_FILE_SEPARATOR, DIRECTIVE_FILE_SEPARATOR)
} else {
throw new RuntimeException("File was not sourced from the same ScanDirectory #{filePath}")
}
}
/**
* Uses file globbing to scan for files that need precompiled
*/
public Collection scanForFiles(List excludePatterns, List includePatterns) {
def fileList = []
List excludedPatternList = excludePatterns ? excludePatterns : new ArrayList()
List includedPatternList = includePatterns ? includePatterns : new ArrayList()
for(JarEntry entry in baseJar.entries()) {
if(entry.name.startsWith(prefixPath + "/")) {
def relativePath = relativePathToResolver(entry, prefixPath)
if(!isFileMatchingPatterns(relativePath,excludedPatternList) || isFileMatchingPatterns(relativePath,includedPatternList)) {
if(!entry.isDirectory()) {
def assetFileClass = AssetHelper.assetForFileName(relativePath)
if(assetFileClass) {
fileList << assetFileClass.newInstance(inputStreamSource: createInputStreamClosure(entry), path: relativePath, sourceResolver: this)
} else {
fileList << new GenericAssetFile(inputStreamSource: createInputStreamClosure(entry), path: relativePath)
}
}
}
}
}
return fileList.unique { AssetFile a, AssetFile b -> a.path <=> b.path }
}
}