blob: 0ba1ce0fd97d1530bcfde3136f915b912bba3be5 [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.sling.distribution.journal.impl.shared;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.Duration;
import java.util.List;
import java.util.Optional;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.felix.webconsole.AbstractWebConsolePlugin;
import org.apache.felix.webconsole.WebConsoleConstants;
import org.apache.sling.distribution.journal.FullMessage;
import org.apache.sling.distribution.journal.messages.Messages.PackageMessage;
import org.apache.sling.distribution.journal.messages.Messages.PackageMessage.ReqType;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(service = Servlet.class,
property = {
Constants.SERVICE_DESCRIPTION + "=" + "Package viewer for content distribution",
WebConsoleConstants.PLUGIN_LABEL + "=" + PackageViewerPlugin.LABEL,
WebConsoleConstants.PLUGIN_TITLE + "=" + PackageViewerPlugin.TITLE
})
public class PackageViewerPlugin extends AbstractWebConsolePlugin {
private static final Duration BROWSE_TIMEOUT = Duration.ofMillis(1000);
private static final Duration DOWNLOAD_TIMEOUT = Duration.ofMillis(20000);
private static final int NOT_FOUND = 404;
private static final int MAX_NUM_MESSAGES = 100;
private static final long serialVersionUID = -3113699912185558439L;
protected static final String LABEL = "distpackages";
protected static final String TITLE = "Distribution Package Viewer";
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Reference
PackageBrowser packageBrowser; //NOSONAR
@Override
public String getLabel() {
return LABEL;
}
@Override
public String getTitle() {
return TITLE;
}
@Override
public String getCategory() {
return "Sling";
}
@Override
protected void renderContent(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
Optional<Long> offset = getOffset(req);
if (!offset.isPresent()) {
String startOffsetSt = req.getParameter("startOffset");
long startOffset = startOffsetSt != null ? new Long(startOffsetSt) : 0;
renderPackageList(startOffset, res.getWriter());
} else {
writePackage(offset.get(), res);
}
}
private void renderPackageList(long startOffset, PrintWriter writer) {
writer.println("<table class=\"tablesorter nicetable noauto\">");
writer.println("<tr><th>Id</th><th>Offset</th><th>Type</th><th>Paths</th></tr>");
List<FullMessage<PackageMessage>> msgs = packageBrowser.getMessages(startOffset, MAX_NUM_MESSAGES, BROWSE_TIMEOUT);
msgs.stream().filter(this::notTestMessage).map(this::writeMsg).forEach(writer::println);
writer.println("</table>");
if (msgs.size() == MAX_NUM_MESSAGES) {
FullMessage<PackageMessage> lastMsg = msgs.get(msgs.size() - 1);
long nextOffset = lastMsg.getInfo().getOffset() + 1;
writer.println(String.format("<p><a href =\"%s?startOffset=%d\">next page</a>",
LABEL,
nextOffset));
}
}
private String writeMsg(FullMessage<PackageMessage> msg) {
return String.format("<tr><td><a href=\"%s/%d\">%s</a></td><td>%d</td><td>%s</td><td>%s</td></tr>",
LABEL,
msg.getInfo().getOffset(),
msg.getMessage().getPkgId(),
msg.getInfo().getOffset(),
msg.getMessage().getReqType(),
msg.getMessage().getPathsList().toString());
}
private void writePackage(Long offset, HttpServletResponse res) throws IOException {
log.info("Retrieving package with offset {}", offset);
List<FullMessage<PackageMessage>> msgs = packageBrowser.getMessages(offset, 1, DOWNLOAD_TIMEOUT);
if (!msgs.isEmpty()) {
PackageMessage msg = msgs.iterator().next().getMessage();
res.setHeader("Content-Type", "application/octet-stream");
String filename = msg.getPkgId() + ".zip";
res.setHeader("Content-Disposition" , "inline; filename=\"" + filename + "\"");
packageBrowser.writeTo(msg, res.getOutputStream());
} else {
res.setStatus(NOT_FOUND);
}
}
@Override
protected boolean isHtmlRequest(HttpServletRequest request) {
return !getOffset(request).isPresent();
}
private Optional<Long> getOffset(HttpServletRequest req) {
int startIndex = LABEL.length() + 2;
if (startIndex <= req.getPathInfo().length()) {
String offsetSt = req.getPathInfo().substring(startIndex);
return Optional.of(new Long(offsetSt));
} else {
return Optional.empty();
}
}
private boolean notTestMessage(FullMessage<PackageMessage> msg) {
return msg.getMessage().getReqType() != ReqType.TEST;
}
}