1
|
/*
|
2
|
* z2-Environment
|
3
|
*
|
4
|
* Copyright(c) ZFabrik Software KG
|
5
|
*
|
6
|
* contact@zfabrik.de
|
7
|
*
|
8
|
* http://www.z2-environment.eu
|
9
|
*/
|
10
|
package com.zfabrik.impl.javadoc;
|
11
|
|
12
|
import java.io.BufferedWriter;
|
13
|
import java.io.File;
|
14
|
import java.io.FileFilter;
|
15
|
import java.io.FileWriter;
|
16
|
import java.io.IOException;
|
17
|
import java.lang.reflect.Method;
|
18
|
import java.net.URL;
|
19
|
import java.net.URLClassLoader;
|
20
|
import java.util.HashSet;
|
21
|
import java.util.LinkedList;
|
22
|
import java.util.List;
|
23
|
import java.util.Set;
|
24
|
import java.util.StringTokenizer;
|
25
|
import java.util.logging.Logger;
|
26
|
|
27
|
import com.zfabrik.components.IComponentDescriptor;
|
28
|
import com.zfabrik.components.IComponentsLookup;
|
29
|
import com.zfabrik.components.IComponentsManager;
|
30
|
import com.zfabrik.components.java.IJavaComponent;
|
31
|
import com.zfabrik.components.java.JavaComponentUtil;
|
32
|
import com.zfabrik.components.provider.util.LockingRevFile;
|
33
|
import com.zfabrik.util.fs.FileUtils;
|
34
|
import com.zfabrik.util.html.Escaper;
|
35
|
|
36
|
public class ComponentJavaDoc {
|
37
|
public final static short TYPE_API = 1;
|
38
|
public final static short TYPE_IMPL = 2;
|
39
|
public final static short TYPE_BOTH = 3;
|
40
|
|
41
|
private static final FileFilter FOLDER_OR_JAVA_AND_NOT_HIDDEN = new FileFilter() {
|
42
|
public boolean accept(File pathname) {
|
43
|
return (pathname.isDirectory() || pathname.getName().endsWith(".java")) && !pathname.getName().startsWith(".");
|
44
|
}
|
45
|
};
|
46
|
|
47
|
private static final String IMPLEMENTATION_VERSION = "11";
|
48
|
|
49
|
private static class InvokeContext {
|
50
|
ClassLoader cl;
|
51
|
Method m;
|
52
|
}
|
53
|
|
54
|
private short type;
|
55
|
private String component;
|
56
|
private boolean resolved;
|
57
|
private File root;
|
58
|
|
59
|
public ComponentJavaDoc(String component,short type) {
|
60
|
super();
|
61
|
this.type = type;
|
62
|
this.component = component;
|
63
|
}
|
64
|
|
65
|
|
66
|
public File getRootFolder() {
|
67
|
this.resolve();
|
68
|
return this.root;
|
69
|
}
|
70
|
|
71
|
private File resolve() {
|
72
|
if (!this.resolved) {
|
73
|
this.resolved = true;
|
74
|
this.root = getJavaDoc(this.component,this.type);
|
75
|
}
|
76
|
return this.root;
|
77
|
}
|
78
|
|
79
|
private static File getJavaDocFolderInComponentFolder(File cf, short type) {
|
80
|
switch (type) {
|
81
|
case TYPE_IMPL:
|
82
|
return new File(cf,"gen/doc.impl");
|
83
|
case TYPE_API:
|
84
|
return new File(cf,"gen/doc.api");
|
85
|
case TYPE_BOTH:
|
86
|
return new File(cf,"gen/doc.both");
|
87
|
}
|
88
|
throw new IllegalArgumentException();
|
89
|
}
|
90
|
|
91
|
private static synchronized File getJavaDoc(String component, short type) {
|
92
|
try {
|
93
|
// need to check.
|
94
|
// rely on the java components logic. Look it up as Java Component to
|
95
|
// force compilation check
|
96
|
IJavaComponent jc = IComponentsLookup.INSTANCE.lookup(component, IJavaComponent.class);
|
97
|
if (jc!=null) {
|
98
|
IComponentDescriptor desc = IComponentsManager.INSTANCE.getComponent(component);
|
99
|
File croot = IComponentsManager.INSTANCE.retrieve(component);
|
100
|
File jdoc = getJavaDocFolderInComponentFolder(croot, type);
|
101
|
if (Boolean.parseBoolean(desc.getProperty(IJavaComponent.NOBUILD))) {
|
102
|
// no jdoc gen for no build components
|
103
|
if (jdoc.exists()) {
|
104
|
return jdoc;
|
105
|
}
|
106
|
return null;
|
107
|
} else {
|
108
|
// check revision
|
109
|
String revfile = "doc_"+qualifier(type)+".rev";
|
110
|
LockingRevFile lrf = new LockingRevFile(new File(croot,revfile));
|
111
|
lrf.open();
|
112
|
try {
|
113
|
if (jdoc.exists() && IMPLEMENTATION_VERSION.equals(lrf.properties().get("v"))) {
|
114
|
// javadocs exist, no build component or right version
|
115
|
return jdoc;
|
116
|
} else {
|
117
|
logger.info("Updating Javadoc for component "+component+" ("+qualifier(type)+")");
|
118
|
// no jdocs or wrong implementation version
|
119
|
// must generate javadocs
|
120
|
// check javadoc tool
|
121
|
InvokeContext ic = getTool();
|
122
|
if (ic==null) {
|
123
|
return null;
|
124
|
}
|
125
|
// compute params
|
126
|
List<String> params = new LinkedList<String>();
|
127
|
List<File> files = new LinkedList<File>();
|
128
|
|
129
|
// 1. source paths and packages
|
130
|
List<File> fs = new LinkedList<File>();
|
131
|
if ((type & TYPE_IMPL)!= 0) {
|
132
|
File sf = new File(croot,"src.impl");
|
133
|
if (sf.exists()) {
|
134
|
fs.add(sf);
|
135
|
}
|
136
|
}
|
137
|
if ((type & TYPE_API)!= 0) {
|
138
|
File sf = new File(croot,"src.api");
|
139
|
if (sf.exists()) {
|
140
|
fs.add(sf);
|
141
|
}
|
142
|
}
|
143
|
|
144
|
if (!fs.isEmpty()) {
|
145
|
// 1a. ok, there is something to do
|
146
|
StringBuilder sourcepath = new StringBuilder(200);
|
147
|
for (File f : fs) {
|
148
|
// add as source path and
|
149
|
// list all packages
|
150
|
int l = files.size();
|
151
|
addSourceFiles(files,f);
|
152
|
if (l<files.size()) {
|
153
|
sourcepath.append((sourcepath.length()>0? File.pathSeparator:"")).append(f.getCanonicalPath());
|
154
|
}
|
155
|
}
|
156
|
params.add("-sourcepath");
|
157
|
params.add(sourcepath.toString());
|
158
|
|
159
|
// 2. deps translate to -linkoffline relations
|
160
|
addLinks(component, new HashSet<String>(), params, type, true);
|
161
|
|
162
|
params.add("-link");
|
163
|
params.add("http://download.oracle.com/javase/6/docs/api");
|
164
|
|
165
|
// 3. class path
|
166
|
StringBuilder classpath = new StringBuilder(200);
|
167
|
// add the classpath
|
168
|
addToPath(classpath, ((type & TYPE_IMPL)!=0? jc.getPrivateLoader().getURLs() : jc.getPublicLoader().getURLs()));
|
169
|
|
170
|
params.add("-classpath");
|
171
|
params.add(classpath.toString());
|
172
|
|
173
|
// 4. outpath
|
174
|
FileUtils.delete(jdoc);
|
175
|
jdoc.mkdirs();
|
176
|
params.add("-d");
|
177
|
params.add(jdoc.getCanonicalPath());
|
178
|
|
179
|
if (!files.isEmpty()) {
|
180
|
// 5. add files to generate javadoc for
|
181
|
File fileList = File.createTempFile("javadoc_",".lst");
|
182
|
BufferedWriter w = new BufferedWriter(new FileWriter(fileList));
|
183
|
try {
|
184
|
for (File sf : files) {
|
185
|
w.write(sf.getAbsolutePath());
|
186
|
w.newLine();
|
187
|
}
|
188
|
} finally {
|
189
|
w.close();
|
190
|
}
|
191
|
params.add("@"+fileList.getAbsolutePath());
|
192
|
|
193
|
try {
|
194
|
// 6. and GO!
|
195
|
logger.info("Running Javadoc command with command line: "+params);
|
196
|
ClassLoader ol = Thread.currentThread().getContextClassLoader();
|
197
|
Thread.currentThread().setContextClassLoader(ic.cl);
|
198
|
try {
|
199
|
|
200
|
int rc = (Integer) ic.m.invoke(null, new Object[]{params.toArray(new String[params.size()])});
|
201
|
if (rc==0 && !empty(jdoc)) {
|
202
|
// done
|
203
|
lrf.properties().setProperty("v", IMPLEMENTATION_VERSION);
|
204
|
lrf.update();
|
205
|
return jdoc;
|
206
|
}
|
207
|
} finally {
|
208
|
Thread.currentThread().setContextClassLoader(ol);
|
209
|
}
|
210
|
} finally {
|
211
|
fileList.delete();
|
212
|
}
|
213
|
} else {
|
214
|
logger.info("Found no packages: Nothing to do");
|
215
|
}
|
216
|
}
|
217
|
}
|
218
|
} finally {
|
219
|
lrf.close();
|
220
|
}
|
221
|
}
|
222
|
}
|
223
|
} catch (Exception e) {
|
224
|
throw new RuntimeException("Javadoc generation failed",e);
|
225
|
}
|
226
|
return null;
|
227
|
}
|
228
|
|
229
|
|
230
|
private static String qualifier(short type) {
|
231
|
return type==TYPE_IMPL?"impl":type==TYPE_API?"api":"both";
|
232
|
}
|
233
|
|
234
|
|
235
|
private static void addToPath(StringBuilder classpath, URL[] urls) {
|
236
|
if (urls!=null) {
|
237
|
for (URL u : urls) {
|
238
|
if (classpath.length()>0) {
|
239
|
classpath.append(File.pathSeparator);
|
240
|
}
|
241
|
classpath.append(u.getPath());
|
242
|
}
|
243
|
}
|
244
|
}
|
245
|
|
246
|
private static boolean empty(File tf) {
|
247
|
return (!tf.isDirectory() || tf.list().length==0);
|
248
|
}
|
249
|
|
250
|
|
251
|
//
|
252
|
// find the actual javadoc implementation
|
253
|
//
|
254
|
private static InvokeContext getTool() throws Exception {
|
255
|
String jhs = System.getenv("JAVA_HOME");
|
256
|
if (jhs==null) {
|
257
|
jhs = System.getProperty("java.home");
|
258
|
if (jhs.endsWith("/jre")) {
|
259
|
// try one level up
|
260
|
jhs = jhs.substring(0,jhs.length()-4);
|
261
|
}
|
262
|
}
|
263
|
File jh = new File(jhs);
|
264
|
File tj = new File(jh,"lib/tools.jar");
|
265
|
ClassLoader cl = ComponentJavaDoc.class.getClassLoader();
|
266
|
if (!tj.exists()) {
|
267
|
logger.warning("Library tools.jar not found at "+tj.getCanonicalPath()+" will try to load Javadoc tool directly");
|
268
|
} else {
|
269
|
cl = new URLClassLoader(new URL[]{tj.toURI().toURL()}, cl);
|
270
|
}
|
271
|
try {
|
272
|
Class<?> jdclz = Class.forName("com.sun.tools.javadoc.Main",false,cl);
|
273
|
InvokeContext r = new InvokeContext();
|
274
|
r.cl = cl;
|
275
|
r.m=jdclz.getMethod("execute",String[].class);
|
276
|
return r;
|
277
|
} catch (Exception e) {
|
278
|
logger.warning("Failed to load JavaDoc tool class (are you running a JDK?) - cannot update javadocs ("+e+")");
|
279
|
return null;
|
280
|
}
|
281
|
}
|
282
|
|
283
|
//
|
284
|
// recursively find package names in a folder hierarchy
|
285
|
//
|
286
|
private static void addSourceFiles(List<File> files, File f) {
|
287
|
for (File g : f.listFiles(FOLDER_OR_JAVA_AND_NOT_HIDDEN)) {
|
288
|
if (g.isDirectory()) {
|
289
|
addSourceFiles(files,g);
|
290
|
} else {
|
291
|
files.add(g);
|
292
|
}
|
293
|
}
|
294
|
}
|
295
|
|
296
|
//
|
297
|
// add external refs as link params following the
|
298
|
// java refs
|
299
|
//
|
300
|
private static void addLinks(String component, Set<String> traversed, List<String> params, short type, boolean start) throws IOException {
|
301
|
component = JavaComponentUtil.fixJavaComponentName(component);
|
302
|
if (traversed.contains(component+"@"+type)) {
|
303
|
// already traversed.
|
304
|
return;
|
305
|
}
|
306
|
traversed.add(component+"@"+type);
|
307
|
|
308
|
IComponentDescriptor desc = IComponentsManager.INSTANCE.getComponent(component);
|
309
|
if (desc==null) {
|
310
|
return;
|
311
|
}
|
312
|
|
313
|
// deps
|
314
|
addAllLinks(traversed, params, desc.getProperty(IJavaComponent.PUBREFS));
|
315
|
if (start && (type&TYPE_IMPL)!=0) {
|
316
|
// add private references
|
317
|
addAllLinks(traversed, params, desc.getProperty(IJavaComponent.PRIREFS));
|
318
|
// add our own API
|
319
|
addLinks(component, traversed, params, TYPE_API,false);
|
320
|
}
|
321
|
|
322
|
if (start) {
|
323
|
// at start of recursion add the always present core api
|
324
|
// add the core api
|
325
|
addLinks("com.zfabrik.core.api",traversed,params,TYPE_API,false);
|
326
|
} else {
|
327
|
// otherwise add the actual API
|
328
|
File f = getJavaDoc(component, TYPE_API);
|
329
|
if (f!=null) {
|
330
|
params.add("-linkoffline");
|
331
|
params.add("/javadoc/"+Escaper.urlEncode(component,'!')+"/api");
|
332
|
params.add(f.toURI().toString());
|
333
|
}
|
334
|
}
|
335
|
}
|
336
|
|
337
|
|
338
|
private static void addAllLinks(Set<String> traversed, List<String> params,String refs) throws IOException {
|
339
|
if (refs!=null) {
|
340
|
StringTokenizer tk = new StringTokenizer(refs);
|
341
|
while (tk.hasMoreTokens()) {
|
342
|
addLinks(tk.nextToken().trim(), traversed, params, TYPE_API,false);
|
343
|
}
|
344
|
}
|
345
|
}
|
346
|
|
347
|
|
348
|
private final static Logger logger = Logger.getLogger(ComponentJavaDoc.class.getName());
|
349
|
}
|