1 package com.atlassian.plugin.spring.scanner.core;
2
3 import com.atlassian.plugin.spring.scanner.annotation.Profile;
4 import javassist.bytecode.ClassFile;
5 import org.slf4j.Logger;
6
7 import java.io.BufferedInputStream;
8 import java.io.DataInputStream;
9 import java.io.File;
10 import java.io.FileInputStream;
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.net.URISyntaxException;
14 import java.net.URL;
15 import java.util.Collections;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Set;
19 import java.util.TreeMap;
20 import java.util.TreeSet;
21
22
23
24
25
26 class ProfileFinder {
27 private static final String PACKAGE_INFO = "package-info";
28 private final Set<URL> buildPath;
29 private final Logger log;
30 private final JavassistHelper javassistHelper;
31 private final Map<String, Set<String>> mapOfPackagesToProfiles = new TreeMap<String, Set<String>>();
32
33 public ProfileFinder(final Set<URL> buildPath, final Logger log) {
34 this.buildPath = buildPath;
35 this.log = log;
36 this.javassistHelper = new JavassistHelper();
37 }
38
39 boolean isPackageClass(ClassFile classFile) {
40 return classFile.getName().endsWith("." + PACKAGE_INFO);
41 }
42
43
44
45
46
47
48
49 Set<String> getProfiles(ClassFile classFile) {
50 String classFileName = classFile.getName();
51 if (log.isDebugEnabled()) {
52 log.debug(String.format("Checking for profile annotations on %s", classFileName));
53 }
54
55 if (isPackageClass(classFile)) {
56 recordPackageClassProfileAnnotations(classFile);
57 return Collections.emptySet();
58 } else {
59 Set<String> packageInfoProfileAnnotations = getPackageInfoProfileAnnotations(classFile);
60 Set<String> classSpecificProfiles = findProfiles(classFile);
61
62
63 if (!classSpecificProfiles.isEmpty()) {
64 return classSpecificProfiles;
65 }
66 return packageInfoProfileAnnotations;
67 }
68 }
69
70
71
72
73
74
75 private void recordPackageClassProfileAnnotations(final ClassFile packageInfoClassFile) {
76 String packageName = getClassPackageAsFileName(packageInfoClassFile);
77 Set<String> profiles = mapOfPackagesToProfiles.get(packageName);
78
79
80
81
82 if (profiles == null) {
83 mapOfPackagesToProfiles.put(packageName, findProfiles(packageInfoClassFile));
84 }
85 }
86
87 private Set<String> getPackageInfoProfileAnnotations(final ClassFile classFile) {
88
89
90 String packageName = getClassPackageAsFileName(classFile);
91 Set<String> profiles = mapOfPackagesToProfiles.get(packageName);
92 if (profiles != null) {
93 return profiles;
94 }
95
96
97
98 ClassFile packageInfoClassFile = loadPackageInfoClass(packageName);
99 if (packageInfoClassFile != null) {
100 recordPackageClassProfileAnnotations(packageInfoClassFile);
101 } else {
102
103
104 mapOfPackagesToProfiles.put(packageName, Collections.<String>emptySet());
105 }
106
107
108 return mapOfPackagesToProfiles.get(packageName);
109 }
110
111 private ClassFile loadPackageInfoClass(final String packageName) {
112 for (URL url : buildPath) {
113 try {
114 File parentFile = new File(url.toURI());
115 File packageInfoFile = new File(parentFile, packageName + "/" + "package-info.class");
116 if (!packageInfoFile.exists()) {
117 continue;
118 }
119
120 try (InputStream inputStream = new FileInputStream(packageInfoFile)) {
121 DataInputStream dis = new DataInputStream(new BufferedInputStream(inputStream));
122 return new ClassFile(dis);
123 } catch (IOException e) {
124 log.error(String.format("Could not create class file from %s", packageInfoFile), e);
125 }
126 } catch (URISyntaxException e) {
127 log.error(String.format("Unable to load package-info file %s from %s", packageName, url));
128 }
129 }
130 return null;
131 }
132
133 private Set<String> findProfiles(final ClassFile classFile) {
134 List<String> classAnnotationNames = javassistHelper.getClassAnnotationNames(classFile);
135 Set<String> profiles = new TreeSet<String>();
136 for (String annotationType : classAnnotationNames) {
137 if (Profile.class.getCanonicalName().equals(annotationType)) {
138 Set<String> profileValues = javassistHelper.getAnnotationMemberSet(classFile, annotationType, "value");
139 profiles.addAll(profileValues);
140 }
141
142
143 if ("org.springframework.context.annotation.Profile".equals(annotationType)) {
144 Set<String> profileValues = javassistHelper.getAnnotationMemberSet(classFile, annotationType, "value");
145 profiles.addAll(profileValues);
146 }
147 }
148 return profiles;
149 }
150
151 private String getClassPackageAsFileName(final ClassFile classFile) {
152 String fileName = classFile.getName();
153 int lastDot = fileName.lastIndexOf('.');
154 if (lastDot == -1) {
155 return fileName;
156 }
157 String packageName = fileName.substring(0, lastDot);
158 return packageName.replace('.', '/');
159 }
160 }