/* * Copyright 2014 original 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 org.grails.io.support; import grails.util.Environment; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Used to locate resources at build / development time * * @author Graeme Rocher * @since 3.0 */ public class ResourceLocator { public static final String WILDCARD = "*"; public static final String FILE_SEPARATOR = File.separator; public static final String CLOSURE_MARKER = "$"; public static final String WEB_APP_DIR = "web-app"; protected static final Resource NULL_RESOURCE = new ByteArrayResource("null".getBytes()); protected PathMatchingResourcePatternResolver patchMatchingResolver; protected List<String> classSearchDirectories = new ArrayList<String>(); protected List<String> resourceSearchDirectories = new ArrayList<String>(); protected Map<String, Resource> classNameToResourceCache = new ConcurrentHashMap<String, Resource>(); protected Map<String, Resource> uriToResourceCache = new ConcurrentHashMap<String, Resource>(); protected ResourceLoader defaultResourceLoader = new FileSystemResourceLoader(); protected boolean warDeployed = Environment.isWarDeployed(); public void setSearchLocation(String searchLocation) { ResourceLoader resourceLoader = getDefaultResourceLoader(); patchMatchingResolver = new PathMatchingResourcePatternResolver(resourceLoader); initializeForSearchLocation(searchLocation); } protected ResourceLoader getDefaultResourceLoader() { return defaultResourceLoader; } public void setSearchLocations(Collection<String> searchLocations) { patchMatchingResolver = new PathMatchingResourcePatternResolver(getDefaultResourceLoader()); for (String searchLocation : searchLocations) { initializeForSearchLocation(searchLocation); } } private void initializeForSearchLocation(String searchLocation) { String searchLocationPlusSlash = searchLocation.endsWith("/") ? searchLocation : searchLocation + FILE_SEPARATOR; try { File[] directories = new File(searchLocationPlusSlash + GrailsResourceUtils.GRAILS_APP_DIR).listFiles(new FileFilter() { public boolean accept(File file) { return file.isDirectory() && !file.isHidden(); } }); if (directories != null) { for (File directory : directories) { classSearchDirectories.add(directory.getCanonicalPath()); } } } catch (IOException e) { // ignore } classSearchDirectories.add(searchLocationPlusSlash + "src/main/java"); classSearchDirectories.add(searchLocationPlusSlash + "src/main/groovy"); resourceSearchDirectories.add(searchLocationPlusSlash); } public Resource findResourceForURI(String uri) { Resource resource = uriToResourceCache.get(uri); if (resource == null) { PluginResourceInfo info = inferPluginNameFromURI(uri); if (warDeployed) { Resource defaultResource = defaultResourceLoader.getResource(uri); if (defaultResource != null && defaultResource.exists()) { resource = defaultResource; } } else { String uriWebAppRelative = WEB_APP_DIR + uri; for (String resourceSearchDirectory : resourceSearchDirectories) { Resource res = resolveExceptionSafe(resourceSearchDirectory + uriWebAppRelative); if (res.exists()) { resource = res; } else if (!warDeployed) { Resource dir = resolveExceptionSafe(resourceSearchDirectory); if (dir.exists() && info != null) { String filename = dir.getFilename(); if (filename != null && filename.equals(info.pluginName)) { Resource pluginFile = dir.createRelative(WEB_APP_DIR + info.uri); if (pluginFile.exists()) { resource = pluginFile; } } } } } } if (resource == null || !resource.exists()) { Resource tmp = defaultResourceLoader != null ? defaultResourceLoader.getResource(uri) : null; if (tmp != null && tmp.exists()) { resource = tmp; } } if (resource != null) { uriToResourceCache.put(uri, resource); } else if (warDeployed) { uriToResourceCache.put(uri, NULL_RESOURCE); } } return resource == NULL_RESOURCE ? null : resource; } private PluginResourceInfo inferPluginNameFromURI(String uri) { if (uri.startsWith("/plugins/")) { String withoutPluginsPath = uri.substring("/plugins/".length(), uri.length()); int i = withoutPluginsPath.indexOf('/'); if (i > -1) { PluginResourceInfo info = new PluginResourceInfo(); info.pluginName = withoutPluginsPath.substring(0, i); info.uri = withoutPluginsPath.substring(i, withoutPluginsPath.length()); return info; } } return null; } public Resource findResourceForClassName(String className) { if (className.contains(CLOSURE_MARKER)) { className = className.substring(0, className.indexOf(CLOSURE_MARKER)); } Resource resource = classNameToResourceCache.get(className); if (resource == null) { String classNameWithPathSeparator = className.replace(".", FILE_SEPARATOR); for (String pathPattern : getSearchPatternForExtension(classNameWithPathSeparator, ".groovy", ".java")) { resource = resolveExceptionSafe(pathPattern); if (resource != null && resource.exists()) { classNameToResourceCache.put(className, resource); break; } } if (resource == null || !resource.exists()) { for(String ext : new String[]{".groovy", ".java"}) { resource = resolveExceptionSafe(GrailsResourceUtils.DOMAIN_DIR_PATH + "**/" + className + ext); if (resource != null && resource.exists()) { classNameToResourceCache.put(className, resource); break; } } } } return resource != null && resource.exists() ? resource : null; } private List<String> getSearchPatternForExtension(String classNameWithPathSeparator, String... extensions) { List<String> searchPatterns = new ArrayList<String>(); for (String extension : extensions) { String filename = classNameWithPathSeparator + extension; for (String classSearchDirectory : classSearchDirectories) { searchPatterns.add(classSearchDirectory + FILE_SEPARATOR + filename); } } return searchPatterns; } private Resource resolveExceptionSafe(String pathPattern) { try { Resource[] resources = patchMatchingResolver.getResources("file:" + pathPattern); if (resources != null && resources.length > 0) { return resources[0]; } } catch (IOException e) { return null; } return null; } public void setResourceLoader(ResourceLoader resourceLoader) { defaultResourceLoader = resourceLoader; } class PluginResourceInfo { String pluginName; String uri; } }