blob: 33779f82868c0e2494bbcef6b918fe91aa37dcc1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomee.website;
import lombok.Data;
import org.apache.openejb.loader.Files;
import org.apache.openejb.loader.IO;
import org.apache.openejb.util.Join;
import org.apache.openejb.util.Pipe;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.apache.openejb.loader.Files.mkdirs;
/**
* For each git repo (source) it will collects the `src/main/java` contents
* into one large source tree and use the Javadoc proccessor directly in code
* to (with Asciidoclet configured) to generate a single javadoc for that repo.
*
* The examples/ directory for each repo will be filtered out and not included
* in the final aggregated javadoc.
*/
public class Javadocs {
private final Sources sources;
private final List<JavadocSource> javadocSources = new ArrayList<>();
public Javadocs(final Sources sources) {
this.sources = sources;
}
/**
* Method is called for each git repo configured
* as a codebase that contributes to the documentation
*
* This is currently active branches of TomEE, but will
* be other sources in the future such as Sheldon
* and Chatterbox
*
* This method recurssively walks the already cloned
* git repo (the Source) and collects all `src/main/java`
* directories.
*
* All the files in these directories are aggregated into
* one single director at `target/<source.name>-java`.
* For example `target/tomee-8.0-java`
*
* We then run the javadoc processor on `target/<source.name>-java`
* and ensure all generated javadoc is placed at:
*
* - `<sources.generated>/<source.name>/javadoc/`
*
* For example if the Source instance represents TomEE 8.0
* and the Sources.getGenerated() returns `target/site-1.0-SNAPSHOT`
* we would generate all the javadoc into this location:
*
* - `target/site-1.0-SNAPSHOT/tomee-8.0/javadoc`
*
* @param source
*/
public void prepare(final Source source) {
final File javaSources = mkdirs(new File(String.format("target/javadocs/%s-src", source.getName())));
copySource(source, source, javaSources);
for (final Source related : source.getRelated()) {
copySource(source, related, javaSources);
}
// This part will be completed later when the perform stage is executed
source.addPerform(() -> {
final File javadocOutput = sources.getGeneratedDestFor(source, "javadoc");
final ProcessBuilder cmd = new ProcessBuilder(
getJavadocCommand().getAbsolutePath(),
"-tag",
"example.en:a:Examples (en):",
"-tag",
"example.es:a:Examples (es):",
"-tag",
"example.pt:a:Examples (pt):",
"-sourcepath",
javaSources.getAbsolutePath(),
"-d",
javadocOutput.getAbsolutePath()
);
Stream.of(javaSources.listFiles())
.filter(File::isDirectory)
.forEach(file -> {
cmd.command().add("-subpackages");
cmd.command().add(file.getName());
});
try {
final Process process = cmd.start();
Pipe.pipe(process);
process.waitFor();
} catch (IOException e) {
throw new IllegalStateException("Command failed");
} catch (InterruptedException e) {
Thread.interrupted();
throw new IllegalStateException("Command failed");
}
final String favicon = getFavicon(source);
// Scrub generated timestamps as it causes 26k needless file updates
// on the svn commit for every time the generator runs
try {
java.nio.file.Files.walk(javadocOutput.toPath())
.map(Path::toFile)
.filter(File::isFile)
.filter(this::isHtml)
.map(Content::from)
.map(Javadocs::removeGeneratedDate)
.map(content -> addFavicon(content, favicon))
.forEach(Content::write);
} catch (IOException e) {
throw new IllegalStateException("Failed to update generated javadoc html");
}
});
}
private String getFavicon(final Source source) {
if (source.getName().startsWith("jakarta")) {
return "/img/jakarta-favicon.ico";
} else if (source.getName().startsWith("microprofile")) {
return "/img/microprofile-favicon.png";
} else {
return "/favicon.png";
}
}
public List<JavadocSource> getJavadocSources() {
return javadocSources;
}
private static Content removeGeneratedDate(final Content content) {
final List<String> lines = Stream.of(content.content.split("\n"))
.filter(line -> !line.contains("<!-- Generated by javadoc"))
.filter(line -> !line.contains("<meta name=\"date\" content=\""))
.collect(Collectors.toList());
final String updated = Join.join("\n", lines) + "\n";
return content.modified(updated);
}
private static Content addFavicon(final Content content, final String favicon) {
final String link = String.format("%n<link rel=\"shortcut icon\" href=\"%s\">%n", favicon);
return content.modified(content.content.replace("</head>", link + "</head>"));
}
@Data
@lombok.AllArgsConstructor
private static class Content {
private final File file;
private final String content;
public Content modified(final String newContent) {
return new Content(file, newContent);
}
public void write() {
try {
IO.copy(IO.read(content), file);
} catch (IOException e) {
throw new UncheckedIOException("Unable to write file: " + file.getAbsolutePath(), e);
}
}
public static Content from(final File file) {
try {
return new Content(file, IO.slurp(file));
} catch (IOException e) {
throw new UncheckedIOException("Unable to read file: " + file.getAbsolutePath(), e);
}
}
}
private void copySource(final Source parent, final Source source, final File javaSources) {
final JavadocSources javadocSources = new JavadocSources();
source.setComponent(JavadocSources.class, javadocSources);
final Predicate<String> wanted = wanted(parent, source);
try {
java.nio.file.Files.walk(source.getDir().toPath())
.map(Path::toFile)
.filter(File::isFile)
.filter(this::isJava)
.filter(source.getJavadocFilter()::include)
.filter(source.getJavadocFilter()::exclude)
.forEach(file -> {
try {
final String relativePath = file.getAbsolutePath().replaceAll(".*/src/main/java/", "");
if (!wanted.test(relativePath)) return;
final File dest = new File(javaSources, relativePath);
Files.mkdirs(dest.getParentFile());
IO.copy(file, dest);
javadocSources.add(new JavadocSource(relativePath, dest));
} catch (IOException e) {
throw new IllegalStateException(e);
}
});
} catch (IOException e) {
throw new IllegalStateException("Failed to aggregate java sources");
}
}
private Predicate<String> wanted(final Source parent, final Source source) {
if (source.getJavadocPackages() != null) return source.getJavadocPackages().asPredicate();
if (parent.getJavadocPackages() != null) return parent.getJavadocPackages().asPredicate();
return s -> true;
}
public static File getJavadocCommand() {
final File java_home = System.getenv("JAVA_HOME") != null ? new File(System.getenv("JAVA_HOME")) : new File("");
final File javaHome = new File(System.getProperty("java.home"));
final Supplier<File>[] locations = new Supplier[]{
() -> new File(java_home, "bin/javadoc"),
() -> new File(java_home, "bin/javadoc.exe"),
() -> new File(java_home.getParentFile(), "bin/javadoc"),
() -> new File(java_home.getParentFile(), "bin/javadoc.exe"),
() -> new File(javaHome, "bin/javadoc"),
() -> new File(javaHome, "bin/javadoc.exe"),
() -> new File(javaHome.getParentFile(), "bin/javadoc"),
() -> new File(javaHome.getParentFile(), "bin/javadoc.exe"),
};
return Stream.of(locations)
.map(Supplier::get)
.filter(File::exists)
.filter(File::canExecute)
.findFirst().orElseThrow(() -> new IllegalStateException("Cannot find javadoc command"));
}
public static void main(String[] args) {
}
private static void copy(final File file, File javaSources) {
}
private boolean isJava(final File file) {
return file.getName().endsWith(".java");
}
private boolean isHtml(final File file) {
return file.getName().endsWith(".html");
}
}