001 /** 002 * Copyright 2010-2013 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 */ 016 package org.kuali.common.util; 017 018 import java.io.File; 019 import java.util.ArrayList; 020 import java.util.Arrays; 021 import java.util.HashMap; 022 import java.util.List; 023 import java.util.Map; 024 import java.util.Properties; 025 import java.util.Set; 026 027 import org.apache.commons.lang3.StringUtils; 028 import org.kuali.common.util.property.Constants; 029 import org.kuali.common.util.property.PropertiesContext; 030 import org.slf4j.Logger; 031 import org.slf4j.LoggerFactory; 032 import org.springframework.util.PropertyPlaceholderHelper; 033 034 /** 035 * 036 */ 037 public class ProjectUtils { 038 039 private static final Logger logger = LoggerFactory.getLogger(ProjectUtils.class); 040 private static final PropertyPlaceholderHelper PPH = Constants.DEFAULT_PROPERTY_PLACEHOLDER_HELPER; 041 private static final String GROUP_ID_BASE_PATH_KEY = "project.groupId.base.path"; 042 private static final String CLASSPATH = "classpath:"; 043 044 @Deprecated 045 public static final String KUALI_COMMON_GROUP_ID = KualiProjectConstants.COMMON_GROUP_ID; 046 047 @Deprecated 048 public static final String KUALI_UTIL_ARTIFACT_ID = UtilProjectContext.ARTIFACT_ID; 049 050 private static final Map<String, Properties> PROJECT_PROPERTIES_CACHE = new HashMap<String, Properties>(); 051 052 public static List<Project> loadProjects(List<String> projectIds) { 053 List<Project> projects = new ArrayList<Project>(); 054 for (String projectId : projectIds) { 055 Project project = ProjectUtils.loadProject(projectId); 056 projects.add(project); 057 } 058 return projects; 059 } 060 061 /** 062 * <pre> 063 * kuali-util = classpath:org/kuali/common/kuali-util 064 * </pre> 065 */ 066 public static String getCommonClassPathPrefix(String artifactId) { 067 return getClassPathPrefix(KualiProjectConstants.COMMON_GROUP_ID, artifactId); 068 } 069 070 /** 071 * Given a groupId and artifactId, convert the groupId to groupId.base, then return the classpath prefix 072 * 073 * <pre> 074 * org.kuali.student.db:ks-impex-rice-db = classpath:org/kuali/student/ks-impex-rice-db 075 * org.kuali.common:kuali-util = classpath:org/kuali/common/kuali-util 076 * </pre> 077 */ 078 public static String getClassPathPrefix(String groupId, String artifactId) { 079 Project project = loadProject(groupId, artifactId); 080 return CLASSPATH + getResourcePath(project); 081 } 082 083 /** 084 * Given groupId:artifactId, convert the groupId to groupId.base, then return the classpath prefix 085 * 086 * <pre> 087 * org.kuali.student.db:ks-impex-rice-db = classpath:org/kuali/student/ks-impex-rice-db 088 * org.kuali.common:kuali-util = classpath:org/kuali/common/kuali-util 089 * </pre> 090 * 091 * Use getClassPathPrefixFromProjectId() instead 092 */ 093 @Deprecated 094 public static String getClassPathPrefixFromGAV(String projectId) { 095 Project project = getProject(projectId); 096 return getClassPathPrefix(project); 097 } 098 099 /** 100 * Given groupId:artifactId, convert the groupId to groupId.base, then return the classpath prefix 101 * 102 * <pre> 103 * org.kuali.student.db:ks-impex-rice-db = classpath:org/kuali/student/ks-impex-rice-db 104 * org.kuali.common:kuali-util = classpath:org/kuali/common/kuali-util 105 * </pre> 106 */ 107 public static String getClassPathPrefixFromProjectId(String projectId) { 108 Project project = getProject(projectId); 109 return getClassPathPrefix(project); 110 } 111 112 /** 113 * Given a project containing groupId + artifactId, convert the groupId to groupId.base, then return the classpath prefix 114 * 115 * <pre> 116 * org.kuali.student.db:ks-impex-rice-db = classpath:org/kuali/student/ks-impex-rice-db 117 * org.kuali.common:kuali-util = classpath:org/kuali/common/kuali-util 118 * </pre> 119 */ 120 public static String getClassPathPrefix(Project project) { 121 return getClassPathPrefix(project.getGroupId(), project.getArtifactId()); 122 } 123 124 /** 125 * Given a groupId and artifactId, convert the groupId to groupId.base, then return a resource path relative to directory 126 * 127 * <pre> 128 * org.kuali.student.db:ks-impex-rice-db = org/kuali/student/ks-impex-rice-db 129 * org.kuali.common:kuali-util = org/kuali/common/kuali-util 130 * 131 * /tmp/x/y/z + org.kuali.common:kuali-util = /tmp/x/y/z/org/kuali/common/kuali-util 132 * </pre> 133 */ 134 public static File getResourceDirectory(File directory, Project project) { 135 String resourcePath = getResourcePath(project); 136 File file = new File(directory, resourcePath); 137 return new File(LocationUtils.getCanonicalPath(file)); 138 } 139 140 /** 141 * Given a groupId and artifactId, convert the groupId to groupId.base, then return a handle to a file relative to directory with the given filename 142 * 143 * <pre> 144 * org.kuali.student.db:ks-impex-rice-db = org/kuali/student/ks-impex-rice-db 145 * org.kuali.common:kuali-util = org/kuali/common/kuali-util 146 * 147 * /tmp/x/y/z + org.kuali.common:kuali-util + foo.txt = /tmp/x/y/z/org/kuali/common/kuali-util/foo.txt 148 * </pre> 149 */ 150 public static File getResourceFile(File directory, Project project, String filename) { 151 File dir = getResourceDirectory(directory, project); 152 return new File(dir, filename); 153 } 154 155 /** 156 * Given groupId:artifactId, convert the groupId to groupId.base, then return a resource friendly prefix 157 * 158 * <pre> 159 * org.kuali.student.db:ks-impex-rice-db = org/kuali/student/ks-impex-rice-db 160 * org.kuali.common:kuali-util = org/kuali/common/kuali-util 161 * </pre> 162 */ 163 public static String getResourcePath(Project project) { 164 Properties properties = project.getProperties(); 165 String groupIdPath = properties.getProperty(GROUP_ID_BASE_PATH_KEY); 166 Assert.hasText(groupIdPath, "groupIdPath has no text"); 167 String artifactId = project.getArtifactId(); 168 return groupIdPath + "/" + artifactId; 169 } 170 171 /** 172 * 173 */ 174 @Deprecated 175 public static org.kuali.common.util.property.ProjectProperties getProjectProperties(ProjectContext context) { 176 177 // Get a project object based on the context information 178 Project project = loadProject(context); 179 180 // Create a properties context object from the project.properties file from META-INF 181 PropertiesContext propertiesContext = new PropertiesContext(project.getProperties()); 182 propertiesContext.setEncoding(project.getEncoding()); 183 propertiesContext.setLocations(context.getPropertyLocations()); 184 185 // Return a project properties object 186 return new org.kuali.common.util.property.ProjectProperties(project, propertiesContext); 187 } 188 189 /** 190 * Create a <code>Project</code> object from the <code>context</code>. This includes loading the corresponding <code>project.properties</code> file from disk. 191 */ 192 @Deprecated 193 public static Project loadProject(ProjectContext context) { 194 return loadProject(getGav(context)); 195 } 196 197 @Deprecated 198 public static String getGav(ProjectContext context) { 199 return getGav(context.getGroupId(), context.getArtifactId()); 200 } 201 202 @Deprecated 203 public static String getGav(Project project) { 204 return getGav(project.getGroupId(), project.getArtifactId()); 205 } 206 207 @Deprecated 208 public static String getGav(String groupId, String artifactId) { 209 return groupId + ":" + artifactId; 210 } 211 212 public static String getProjectId(Project project) { 213 return getProjectId(project.getGroupId(), project.getArtifactId()); 214 } 215 216 public static String getProjectId(String groupId, String artifactId) { 217 return groupId + ":" + artifactId; 218 } 219 220 /** 221 * Create a <code>Project</code> object from <code>groupId</code>, <code>artifactId</code> pair. This includes loading the corresponding <code>project.properties</code> file 222 * from disk. 223 */ 224 public static Project loadProject(String groupId, String artifactId) { 225 String projectId = getProjectId(groupId, artifactId); 226 return loadProject(projectId); 227 } 228 229 /** 230 * Create a <code>Project</code> object from the <code>projectId</code>. This includes loading the corresponding <code>project.properties</code> file from disk. 231 */ 232 public static Project loadProject(String projectId) { 233 // Convert the projectId into a Project 234 Project project = getProject(projectId); 235 236 // Load properties from a .properties file for this project 237 Properties properties = loadProperties(project); 238 239 // Return a fully configured project object based on the properties 240 Project loadedProject = getProject(properties); 241 242 // return the project we loaded 243 return loadedProject; 244 } 245 246 /** 247 * Provide a way to clear the cache 248 */ 249 public synchronized static void clearCache() { 250 PROJECT_PROPERTIES_CACHE.clear(); 251 } 252 253 /** 254 * Create a skeleton <code>Project</code> object from the <code>gav</code>. Nothing but the GAV info (groupId:artifactId:packaging:version:classifier) gets filled in. Does not 255 * read <code>project.properties</code> from disk. 256 */ 257 public static Project getProject(String gav) { 258 logger.debug("Processing [{}]", gav); 259 String[] tokens = StringUtils.split(gav, ":"); 260 261 Project project = new Project(); 262 if (tokens.length > 0) { 263 project.setGroupId(RepositoryUtils.toNull(tokens[0])); 264 } 265 if (tokens.length > 1) { 266 project.setArtifactId(RepositoryUtils.toNull(tokens[1])); 267 } 268 if (tokens.length > 2) { 269 project.setPackaging(RepositoryUtils.toNull(tokens[2])); 270 } 271 if (tokens.length > 3) { 272 project.setVersion(RepositoryUtils.toNull(tokens[3])); 273 } 274 if (tokens.length > 4) { 275 project.setClassifier(RepositoryUtils.toNull(tokens[4])); 276 } 277 return project; 278 } 279 280 public static List<Dependency> getDependencies(String csv) { 281 List<String> tokens = CollectionUtils.getTrimmedListFromCSV(csv); 282 List<Dependency> dependencies = new ArrayList<Dependency>(); 283 for (String token : tokens) { 284 Dependency dependency = RepositoryUtils.parseDependency(token); 285 dependencies.add(dependency); 286 } 287 return dependencies; 288 } 289 290 /** 291 * Return a <code>Project</code> object by copying values from the <code>properties</code> object into a <code>Project</code> object. 292 */ 293 public static Project getProject(Properties properties) { 294 List<String> skipKeys = Arrays.asList("project.dependencies"); 295 String startsWith = "project."; 296 List<String> keys = PropertyUtils.getStartsWithKeys(properties, startsWith); 297 Project project = new Project(); 298 project.setProperties(properties); 299 Map<String, Object> description = ReflectionUtils.describe(project); 300 Set<String> beanProperties = description.keySet(); 301 for (String key : keys) { 302 if (skipKeys.contains(key)) { 303 continue; 304 } 305 String value = properties.getProperty(key); 306 String beanProperty = getBeanProperty(key, startsWith); 307 if (beanProperties.contains(beanProperty)) { 308 ReflectionUtils.copyProperty(project, beanProperty, value); 309 } 310 } 311 String csv = RepositoryUtils.toNull(properties.getProperty("project.dependencies")); 312 List<Dependency> dependencies = getDependencies(csv); 313 project.setDependencies(dependencies); 314 return project; 315 } 316 317 protected static String getBeanProperty(String key, String startsWith) { 318 String s = StringUtils.substring(key, startsWith.length()); 319 String[] tokens = StringUtils.split(s, "."); 320 StringBuilder sb = new StringBuilder(); 321 for (int i = 0; i < tokens.length; i++) { 322 String token = tokens[i]; 323 if (i == 0) { 324 sb.append(token); 325 } else { 326 sb.append(StringUtils.capitalize(token)); 327 } 328 } 329 return sb.toString(); 330 } 331 332 public static Properties loadProperties(String gav) { 333 return loadProperties(getProject(gav)); 334 } 335 336 /** 337 * Use the groupId and artifactId from this project to load the corresponding project.properties file and cache it in our internal Map 338 */ 339 public static synchronized Properties loadProperties(Project project) { 340 String projectId = getProjectId(project.getGroupId(), project.getArtifactId()); 341 Properties properties = PROJECT_PROPERTIES_CACHE.get(projectId); 342 if (properties == null) { 343 properties = loadAndCache(project, projectId); 344 } 345 return properties; 346 } 347 348 protected static Properties loadAndCache(Project project, String projectId) { 349 String location = getPropertiesFileLocation(project); 350 351 // If it doesn't exist, we've got issues 352 Assert.exists(location); 353 354 Properties properties = PropertyUtils.load(location); 355 PROJECT_PROPERTIES_CACHE.put(projectId, properties); 356 return properties; 357 } 358 359 public static String getPropertiesFileLocation(Project project) { 360 Assert.hasText(project.getGroupId(), "groupId has no text"); 361 Assert.hasText(project.getArtifactId(), "artifactId has no text"); 362 363 Properties properties = new Properties(); 364 properties.setProperty(Constants.GROUP_ID_BASE_PATH_KEY, Str.getPath(project.getGroupId())); 365 properties.setProperty(Constants.ARTIFACT_ID_KEY, project.getArtifactId()); 366 367 return PPH.replacePlaceholders(Constants.PROJECT_PROPERTIES_LOCATION, properties); 368 } 369 370 }