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