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 }