001/** 002 * Copyright 2010-2014 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.common.util.metainf.service; 017 018import java.io.File; 019import java.io.IOException; 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.Collections; 023import java.util.Comparator; 024import java.util.List; 025import java.util.Properties; 026 027import org.apache.commons.io.FileUtils; 028import org.apache.commons.lang3.StringUtils; 029import org.kuali.common.util.Assert; 030import org.kuali.common.util.CollectionUtils; 031import org.kuali.common.util.FileSystemUtils; 032import org.kuali.common.util.LocationUtils; 033import org.kuali.common.util.PropertyUtils; 034import org.kuali.common.util.file.CanonicalFile; 035import org.kuali.common.util.log.LoggerUtils; 036import org.kuali.common.util.metainf.model.MetaInfContext; 037import org.kuali.common.util.metainf.model.MetaInfResource; 038import org.kuali.common.util.metainf.model.ScanResult; 039import org.kuali.common.util.metainf.model.WriteLines; 040import org.kuali.common.util.metainf.model.WriteProperties; 041import org.kuali.common.util.metainf.model.WriteRequest; 042import org.slf4j.Logger; 043import org.slf4j.LoggerFactory; 044 045public class DefaultMetaInfService implements MetaInfService { 046 047 private static final Logger logger = LoggerFactory.getLogger(DefaultMetaInfService.class); 048 049 protected static final String PROPERTIES = "properties"; 050 protected static final String SIZE = "size"; 051 protected static final String LINES = "lines"; 052 053 @Override 054 public ScanResult scan(MetaInfContext context) { 055 List<File> files = scanFileSystem(context); 056 List<MetaInfResource> resources = getResources(context, files); 057 return new ScanResult(context, resources); 058 } 059 060 @Override 061 public List<ScanResult> scan(List<MetaInfContext> contexts) { 062 List<ScanResult> results = new ArrayList<ScanResult>(); 063 for (MetaInfContext context : contexts) { 064 ScanResult result = scan(context); 065 results.add(result); 066 } 067 return results; 068 } 069 070 @Override 071 public void write(ScanResult result) { 072 write(Arrays.asList(result)); 073 } 074 075 protected WriteLines getWriteLines(ScanResult result) { 076 List<MetaInfResource> resources = result.getResources(); 077 List<String> locations = new ArrayList<String>(); 078 for (MetaInfResource resource : resources) { 079 locations.add(resource.getLocation()); 080 } 081 MetaInfContext context = result.getContext(); 082 File outputFile = context.getOutputFile(); 083 String encoding = context.getEncoding(); 084 File relativeDir = context.getRelativeDir(); 085 WriteRequest request = new WriteRequest(outputFile, encoding, relativeDir); 086 return new WriteLines(request, locations); 087 } 088 089 @Override 090 public void write(List<ScanResult> results) { 091 List<WriteLines> lines = getWriteLines(results); 092 List<WriteProperties> properties = getWriteProperties(results); 093 for (WriteLines element : CollectionUtils.toEmptyList(lines)) { 094 WriteRequest request = element.getRequest(); 095 String relativePath = FileSystemUtils.getRelativePathQuietly(request.getRelativeDir(), request.getOutputFile()); 096 logger.info("Creating [{}] - {} resources", relativePath, element.getLines().size()); 097 write(request, element.getLines()); 098 } 099 for (WriteProperties element : CollectionUtils.toEmptyList(properties)) { 100 WriteRequest request = element.getRequest(); 101 PropertyUtils.store(element.getProperties(), request.getOutputFile(), request.getEncoding()); 102 } 103 } 104 105 protected void write(WriteRequest request, List<String> lines) { 106 try { 107 FileUtils.writeLines(request.getOutputFile(), request.getEncoding(), lines); 108 } catch (IOException e) { 109 throw new IllegalArgumentException("Unexpected IO error", e); 110 } 111 } 112 113 protected List<WriteProperties> getWriteProperties(List<ScanResult> results) { 114 List<WriteProperties> requests = new ArrayList<WriteProperties>(); 115 for (ScanResult result : results) { 116 MetaInfContext context = result.getContext(); 117 if (context.isIncludePropertiesFile()) { 118 WriteProperties request = getWriteProperties(result); 119 requests.add(request); 120 } 121 } 122 return requests; 123 } 124 125 protected WriteProperties getWriteProperties(ScanResult result) { 126 List<MetaInfResource> resources = result.getResources(); 127 Properties properties = new Properties(); 128 for (MetaInfResource resource : resources) { 129 String key = getPropertyKey(resource.getLocation()); 130 String sizeKey = key + "." + SIZE; 131 String linesKey = key + "." + LINES; 132 properties.setProperty(sizeKey, Long.toString(resource.getSize())); 133 properties.setProperty(linesKey, Long.toString(resource.getLineCount())); 134 } 135 MetaInfContext context = result.getContext(); 136 File canonical = new CanonicalFile(context.getOutputFile()); 137 File outputFile = new File(canonical.getPath() + "." + PROPERTIES); 138 String encoding = context.getEncoding(); 139 File relativeDir = context.getRelativeDir(); 140 WriteRequest request = new WriteRequest(outputFile, encoding, relativeDir); 141 return new WriteProperties(request, properties); 142 } 143 144 protected List<WriteLines> getWriteLines(List<ScanResult> results) { 145 List<WriteLines> requests = new ArrayList<WriteLines>(); 146 for (ScanResult result : results) { 147 WriteLines request = getWriteLines(result); 148 requests.add(request); 149 } 150 return requests; 151 } 152 153 154 /** 155 * @deprecated 156 */ 157 @Deprecated 158 protected List<File> scanFileSystem(MetaInfContext context) { 159 File dir = context.getScanDir(); 160 Assert.isExistingDir(dir); 161 logger.debug("Examining [" + LocationUtils.getCanonicalPath(dir) + "]"); 162 List<String> includes = context.getIncludes(); 163 List<String> excludes = context.getExcludes(); 164 logger.debug("Patterns - {}", LoggerUtils.getLogMsg(includes, excludes)); 165 org.kuali.common.util.SimpleScanner scanner = new org.kuali.common.util.SimpleScanner(dir, includes, excludes); 166 return scanner.getFiles(); 167 } 168 169 protected List<MetaInfResource> getResources(MetaInfContext context, List<File> files) { 170 List<MetaInfResource> resources = new ArrayList<MetaInfResource>(); 171 for (File file : files) { 172 MetaInfResource resource = getResource(file, context); 173 resources.add(resource); 174 } 175 if (context.isSort()) { 176 if (context.getComparator().isPresent()) { 177 Comparator<MetaInfResource> comparator = context.getComparator().get(); 178 Collections.sort(resources, comparator); 179 } else { 180 Collections.sort(resources); 181 } 182 } 183 return resources; 184 } 185 186 protected MetaInfResource getResource(File resourceFile, MetaInfContext context) { 187 String location = getLocationURL(new CanonicalFile(resourceFile), context); 188 189 long lineCount = MetaInfResource.UNKNOWN_LINECOUNT; 190 191 // Only read through the file if we've been explicitly configured to do so 192 if (context.isIncludeLineCounts()) { 193 194 // Make sure an encoding has been supplied 195 Assert.noBlanks(context.getEncoding()); 196 197 // Read through the entire file keeping track of how many lines of text we encounter 198 lineCount = LocationUtils.getLineCount(resourceFile, context.getEncoding()); 199 } 200 201 // Create a resource object from the information we've collected 202 return new MetaInfResource(location, resourceFile.length(), lineCount); 203 } 204 205 protected String getLocationURL(CanonicalFile resourceFile, MetaInfContext context) { 206 if (!context.isRelativePaths()) { 207 return LocationUtils.getCanonicalURLString(resourceFile); 208 } else { 209 return getRelativeLocationURL(resourceFile, context); 210 } 211 } 212 213 /** 214 * Get a URL string that can be used to address <code>file</code>. This is usually a Spring pseudo-url classpath location, eg - [<code>classpath:foo/bar.txt</code>] 215 * 216 * @param resourceFile 217 * The file to get a location url for. eg - [<code>/x/y/z/src/main/resources/foo/bar.txt</code>] 218 * @param context 219 * Context information for generating a relative location url. eg - [<code>/x/y/z/src/main/resources</code>] and [<code>classpath:</code>]. 220 * 221 * @return A string representing a fully qualified location URL for <code>file</code>. eg - [<code>classpath:foo/bar.txt</code>] 222 */ 223 protected String getRelativeLocationURL(CanonicalFile resourceFile, MetaInfContext context) { 224 225 // Extract the parent directory 226 CanonicalFile parent = new CanonicalFile(context.getRelativeDir()); 227 228 // Make sure it is an existing directory 229 Assert.isExistingDir(parent); 230 231 // Get a string representing the path to the parent dir 232 String parentPath = parent.getPath(); 233 234 // Get a string representing the path to the resource file 235 String resourcePath = resourceFile.getPath(); 236 237 // Make sure the resource file resides underneath the parent dir 238 Assert.isTrue(StringUtils.contains(resourcePath, parentPath), "[" + resourcePath + "] does not contain [" + parentPath + "]"); 239 240 // Extract the portion of the path to the resource file that is relative to the parent dir 241 int relativePos = parentPath.length() + 1; 242 String relativePath = StringUtils.substring(resourcePath, relativePos); 243 244 // Prepend the prefix and return 245 return context.getUrlPrefix() + relativePath; 246 } 247 248 protected String getPropertyKey(String location) { 249 location = StringUtils.replace(location, ":", "."); 250 location = StringUtils.replace(location, "/", "."); 251 return location; 252 } 253 254}