use of jetbrains.buildServer.web.artifacts.browser.ArtifactTreeElement in project teamcity-rest by JetBrains.
the class ArchiveElement method getStreamingOutput.
public StreamingOutput getStreamingOutput(@Nullable final Long startOffset, @Nullable final Long length, final Supplier<String> detailsForLog) {
if (startOffset != null || length != null) {
throw new IllegalStateException("Partial streaming is not yet supported");
}
return new StreamingOutput() {
public void write(final OutputStream out) throws WebApplicationException {
final ZipArchiveOutputStream resultOutput = new ZipArchiveOutputStream(new BufferedOutputStream(out));
// TW-12815
resultOutput.setEncoding(null);
int errorsCount = 0;
try {
for (ArtifactTreeElement artifact : myArtifacts) {
// todo: need to read-lock artifacts???
if (!artifact.isLeaf()) {
// process a directory
String directoryFullName = artifact.getFullName();
if (!directoryFullName.endsWith("/")) {
directoryFullName += "/";
}
ZipArchiveEntry entry = new ZipArchiveEntry(directoryFullName);
final Long lastModified = artifact.getLastModified();
if (lastModified != null)
entry.setTime(lastModified);
try {
resultOutput.putArchiveEntry(entry);
resultOutput.closeArchiveEntry();
} catch (IOException e) {
errorsCount++;
LOG.warnAndDebugDetails("Error packing directory, ignoring. Directory: '" + artifact.getFullName() + "'", e);
}
}
if (!artifact.isContentAvailable())
continue;
try {
InputStream stream = artifact.getInputStream();
try {
ZipArchiveEntry entry = new ZipArchiveEntry(artifact.getFullName());
final Long lastModified = artifact.getLastModified();
// might need to add more, see com.intellij.util.io.ZipUtil.addFileToZip()
if (lastModified != null)
entry.setTime(lastModified);
resultOutput.putArchiveEntry(entry);
try {
FileUtil.copyStreams(stream, resultOutput);
} finally {
resultOutput.closeArchiveEntry();
}
} finally {
FileUtil.close(stream);
}
} catch (IOException e) {
errorsCount++;
LOG.warnAndDebugDetails("Error packing artifact, ignoring. File: '" + artifact.getFullName() + "'", e);
}
}
} finally {
if (errorsCount > 0) {
LOG.warn("Encountered " + errorsCount + " errors while processing " + detailsForLog.get());
}
try {
resultOutput.close();
} catch (Exception e) {
LOG.warnAndDebugDetails("Error closing archived stream", e);
}
}
}
};
}
use of jetbrains.buildServer.web.artifacts.browser.ArtifactTreeElement in project teamcity-rest by JetBrains.
the class BuildArtifactsFinderTest method testLocatorDirectory.
@Test
public void testLocatorDirectory() {
ArtifactTreeElement element;
List<ArtifactTreeElement> artifacts;
artifacts = getArtifacts("", "directory:any");
assertSize(4, artifacts);
assertContainsByFullName(artifacts, "dir1");
assertContainsByFullName(artifacts, "file.txt");
assertContainsByFullName(artifacts, "archive_nested.zip");
assertContainsByFullName(artifacts, "archive.zip");
element = findElement(artifacts, "archive.zip");
assertTrue(element.isArchive());
assertTrue(element.isLeaf());
assertTrue(element.isContentAvailable());
assertNull(element.getChildren());
artifacts = getArtifacts("", "directory:true");
assertSize(1, artifacts);
assertContainsByFullName(artifacts, "dir1");
artifacts = getArtifacts("", "directory:false");
assertSize(3, artifacts);
assertContainsByFullName(artifacts, "file.txt");
assertContainsByFullName(artifacts, "archive_nested.zip");
assertContainsByFullName(artifacts, "archive.zip");
element = findElement(artifacts, "archive.zip");
assertTrue(element.isArchive());
assertTrue(element.isLeaf());
assertTrue(element.isContentAvailable());
assertNull(element.getChildren());
artifacts = getArtifacts("", "directory:false,browseArchives:true");
assertSize(1, artifacts);
assertContainsByFullName(artifacts, "file.txt");
artifacts = getArtifacts("", "directory:true,browseArchives:true");
assertSize(3, artifacts);
assertContainsByFullName(artifacts, "dir1");
assertContainsByFullName(artifacts, "archive_nested.zip");
assertContainsByFullName(artifacts, "archive.zip");
element = findElement(artifacts, "archive.zip");
assertTrue(element.isArchive());
assertFalse(element.isLeaf());
assertTrue(element.isContentAvailable());
assertSize(2, Lists.newArrayList(element.getChildren()));
}
use of jetbrains.buildServer.web.artifacts.browser.ArtifactTreeElement in project teamcity-rest by JetBrains.
the class FilesSubResource method getZipped.
@GET
@Path("/archived" + "{path:(/.*)?}")
@Produces({ MediaType.WILDCARD })
@ApiOperation(value = "Get specific file zipped.", nickname = "getZippedFile")
public Response getZipped(@PathParam("path") final String path, @QueryParam("basePath") final String basePath, @QueryParam("locator") final String locator, @QueryParam("name") final String name, @Context HttpServletRequest request) {
final String processedPath = myProvider.preprocess(StringUtil.removeLeadingSlash(path));
String actualBasePath = basePath != null ? myProvider.preprocess(basePath) : processedPath;
String finalName = myProvider.preprocess(name);
if (StringUtil.isEmpty(finalName)) {
finalName = myProvider.getArchiveName(processedPath) + ".zip";
}
// include al files recursively by default
String actualLocator = Locator.setDimensionIfNotPresent(locator, BuildArtifactsFinder.DIMENSION_RECURSIVE, "true");
// do not expand archives by default
actualLocator = Locator.setDimensionIfNotPresent(actualLocator, BuildArtifactsFinder.ARCHIVES_DIMENSION_NAME, "false");
final FileApiUrlBuilder urlBuilder = fileApiUrlBuilder(locator, myUrlPrefix);
final List<ArtifactTreeElement> elements = BuildArtifactsFinder.getItems(myProvider.getElement(processedPath), actualBasePath, actualLocator, urlBuilder, myBeanContext.getServiceLocator());
final ArchiveElement archiveElement = new ArchiveElement(elements, finalName);
final Response.ResponseBuilder builder = getContentByStream(archiveElement, request, new StreamingOutputProvider() {
@Override
public boolean isRangeSupported() {
return false;
}
@Override
public StreamingOutput getStreamingOutput(@Nullable final Long startOffset, @Nullable final Long length) {
return archiveElement.getStreamingOutput(startOffset, length, () -> "request " + WebUtil.getRequestDump(request));
}
});
for (ArtifactTreeElement element : elements) {
if (!myProvider.fileContentServed(Util.concatenatePath(actualBasePath, element.getFullName()), request))
break;
}
// see jetbrains.buildServer.web.util.WebUtil.addCacheHeadersForIE and http://youtrack.jetbrains.com/issue/TW-9821 for details)
if (WebUtil.isIE10OrLower(request)) {
builder.header("Cache-Control", "private,must-revalidate");
builder.header("Pragma", "private");
}
return builder.build();
}
use of jetbrains.buildServer.web.artifacts.browser.ArtifactTreeElement in project teamcity-rest by JetBrains.
the class FilesSubResource method getContentByStream.
public static Response.ResponseBuilder getContentByStream(@NotNull final Element element, @NotNull final HttpServletRequest request, @NotNull final StreamingOutputProvider streamingOutputProvider) {
// TeamCity API: need to lock artifacts while reading??? e.g. see JavaDoc for jetbrains.buildServer.serverSide.artifacts.BuildArtifacts.iterateArtifacts()
if (!element.isContentAvailable()) {
throw new NotFoundException("Cannot provide content for '" + element.getFullName() + "' (not a file).");
}
final String rangeHeader = request.getHeader(HttpHeaders.RANGE);
Long fullFileSize = null;
try {
final long size = element.getSize();
if (size >= 0) {
fullFileSize = size;
}
} catch (IllegalStateException e) {
// just do not set size in the case
}
Response.ResponseBuilder builder;
if (StringUtil.isEmpty(rangeHeader)) {
builder = Response.ok().entity(streamingOutputProvider.getStreamingOutput(null, null));
if (fullFileSize != null) {
builder.header(HttpHeaders.CONTENT_LENGTH, fullFileSize);
}
} else {
if (!streamingOutputProvider.isRangeSupported()) {
throw new BadRequestException("Ranged requests are not supported for this entity");
}
try {
HttpByteRange range = new HttpByteRange(rangeHeader, fullFileSize);
// todo: support requests with "Range: bytes=XX-" header and unknown content-length via multipart/byteranges Content-Type including Content-Range fields for each part
if (range.getRangesCount() > 1) {
builder = Response.status(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE).entity("Multiple Range header ranges are not (yet) supported");
} else {
final HttpByteRange.SimpleRange firstRange = range.getSimpleRangesIterator().next();
builder = Response.status(HttpServletResponse.SC_PARTIAL_CONTENT);
final long rangeLength = firstRange.getLength();
builder.entity(streamingOutputProvider.getStreamingOutput(firstRange.getBeginIndex(), rangeLength));
builder.header("Content-Range", range.getContentRangeHeaderValue(firstRange));
if (fullFileSize != null) {
builder.header(HttpHeaders.CONTENT_LENGTH, rangeLength);
}
}
} catch (ParseException e) {
builder = Response.status(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE).entity("Error parsing Range header: " + e.getMessage());
if (fullFileSize != null) {
builder.header("Content-Range", HttpByteRange.getContentRangeHeaderValueFor416Response(fullFileSize));
}
}
}
builder.header("Accept-Ranges", HttpByteRange.RANGE_UNIT_BYTES);
if (TeamCityProperties.getBooleanOrTrue("rest.build.artifacts.setMimeType")) {
builder = builder.type(WebUtil.getMimeType(request, element.getName()));
} else {
builder = builder.type(MediaType.APPLICATION_OCTET_STREAM_TYPE);
}
if (TeamCityProperties.getBooleanOrTrue("rest.build.artifacts.forceContentDisposition.Attachment")) {
// make sure the file is not displayed in the browser (TW-27206)
builder = builder.header("Content-Disposition", WebUtil.getContentDispositionValue(request, "attachment", element.getName()));
} else {
builder = builder.header("Content-Disposition", WebUtil.getContentDispositionValue(request, null, element.getName()));
}
if (element instanceof ArtifactTreeElement) {
final Long lastModified = ((ArtifactTreeElement) element).getLastModified();
if (lastModified != null) {
builder.lastModified(new Date(lastModified));
}
final long size = element.getSize();
// mark ETag as "weak"
builder.header("ETag", "W/\"" + EncryptUtil.md5((size >= 0 ? String.valueOf(size) : "") + (lastModified != null ? lastModified : "")) + "\"");
} else {
final long size = element.getSize();
if (size >= 0) {
// mark ETag as "weak"
builder.header("ETag", "W/\"" + EncryptUtil.md5(String.valueOf(size)) + "\"");
}
}
return builder;
}
use of jetbrains.buildServer.web.artifacts.browser.ArtifactTreeElement in project teamcity-rest by JetBrains.
the class BuildArtifactsFinderTest method testLocatorRecursive.
@Test
public void testLocatorRecursive() {
ArtifactTreeElement element;
List<ArtifactTreeElement> artifacts = getArtifacts("", "recursive:true");
assertSize(5, artifacts);
assertContainsByFullName(artifacts, "dir1");
assertContainsByFullName(artifacts, "archive.zip");
assertContainsByFullName(artifacts, "archive_nested.zip");
assertContainsByFullName(artifacts, "file.txt");
assertContainsByFullName(artifacts, "dir1/file.txt");
element = findElement(artifacts, "archive.zip");
assertTrue(element.isArchive());
assertTrue(element.isLeaf());
assertTrue(element.isContentAvailable());
assertNull(element.getChildren());
artifacts = getArtifacts("", "recursive:true,browseArchives:true");
assertSize(13, artifacts);
assertContainsByFullName(artifacts, "dir1");
assertContainsByFullName(artifacts, "archive.zip");
assertContainsByFullName(artifacts, "archive_nested.zip");
assertContainsByFullName(artifacts, "file.txt");
assertContainsByFullName(artifacts, "dir1/file.txt");
assertContainsByFullName(artifacts, "archive.zip!/a");
assertContainsByFullName(artifacts, "archive.zip!/file4.txt");
assertContainsByFullName(artifacts, "archive.zip!/a/b");
assertContainsByFullName(artifacts, "archive.zip!/a/file1.txt");
assertContainsByFullName(artifacts, "archive.zip!/a/file2.txt");
assertContainsByFullName(artifacts, "archive.zip!/a/b/file3.txt");
assertContainsByFullName(artifacts, "archive_nested.zip!/archive.zip");
assertContainsByFullName(artifacts, "archive_nested.zip!/file4.txt");
element = findElement(artifacts, "archive.zip");
assertTrue(element.isArchive());
assertFalse(element.isLeaf());
assertTrue(element.isContentAvailable());
assertSize(2, Lists.newArrayList(element.getChildren()));
artifacts = getArtifacts("", "recursive:2,browseArchives:true");
assertSize(9, artifacts);
assertContainsByFullName(artifacts, "dir1");
assertContainsByFullName(artifacts, "archive.zip");
assertContainsByFullName(artifacts, "file.txt");
assertContainsByFullName(artifacts, "dir1/file.txt");
assertContainsByFullName(artifacts, "archive.zip!/a");
assertContainsByFullName(artifacts, "archive.zip!/file4.txt");
assertContainsByFullName(artifacts, "archive_nested.zip");
assertContainsByFullName(artifacts, "archive_nested.zip!/archive.zip");
assertContainsByFullName(artifacts, "archive_nested.zip!/file4.txt");
element = findElement(artifacts, "archive.zip");
assertTrue(element.isArchive());
assertFalse(element.isLeaf());
assertTrue(element.isContentAvailable());
assertSize(2, Lists.newArrayList(element.getChildren()));
artifacts = getArtifacts("", "recursive:true,browseArchives:false");
assertSize(5, artifacts);
assertContainsByFullName(artifacts, "dir1");
assertContainsByFullName(artifacts, "archive.zip");
assertContainsByFullName(artifacts, "archive_nested.zip");
assertContainsByFullName(artifacts, "file.txt");
assertContainsByFullName(artifacts, "dir1/file.txt");
}
Aggregations