Search in sources :

Example 1 with ArtifactTreeElement

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);
                }
            }
        }
    };
}
Also used : InputStream(java.io.InputStream) OutputStream(java.io.OutputStream) BufferedOutputStream(java.io.BufferedOutputStream) ZipArchiveOutputStream(org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream) ZipArchiveOutputStream(org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream) StreamingOutput(javax.ws.rs.core.StreamingOutput) IOException(java.io.IOException) BrowserException(jetbrains.buildServer.util.browser.BrowserException) IOException(java.io.IOException) WebApplicationException(javax.ws.rs.WebApplicationException) ZipArchiveEntry(org.apache.commons.compress.archivers.zip.ZipArchiveEntry) ArtifactTreeElement(jetbrains.buildServer.web.artifacts.browser.ArtifactTreeElement) BufferedOutputStream(java.io.BufferedOutputStream)

Example 2 with ArtifactTreeElement

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()));
}
Also used : ArtifactTreeElement(jetbrains.buildServer.web.artifacts.browser.ArtifactTreeElement) Test(org.testng.annotations.Test)

Example 3 with ArtifactTreeElement

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();
}
Also used : HttpServletResponse(javax.servlet.http.HttpServletResponse) FileApiUrlBuilder(jetbrains.buildServer.server.rest.model.files.FileApiUrlBuilder) ArtifactTreeElement(jetbrains.buildServer.web.artifacts.browser.ArtifactTreeElement) ArchiveElement(jetbrains.buildServer.server.rest.data.ArchiveElement) ApiOperation(io.swagger.annotations.ApiOperation)

Example 4 with ArtifactTreeElement

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;
}
Also used : HttpByteRange(jetbrains.buildServer.web.util.HttpByteRange) NotFoundException(jetbrains.buildServer.server.rest.errors.NotFoundException) Date(java.util.Date) HttpServletResponse(javax.servlet.http.HttpServletResponse) BadRequestException(jetbrains.buildServer.server.rest.errors.BadRequestException) ParseException(java.text.ParseException) ArtifactTreeElement(jetbrains.buildServer.web.artifacts.browser.ArtifactTreeElement)

Example 5 with ArtifactTreeElement

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");
}
Also used : ArtifactTreeElement(jetbrains.buildServer.web.artifacts.browser.ArtifactTreeElement) Test(org.testng.annotations.Test)

Aggregations

ArtifactTreeElement (jetbrains.buildServer.web.artifacts.browser.ArtifactTreeElement)9 Test (org.testng.annotations.Test)4 IOException (java.io.IOException)2 InputStream (java.io.InputStream)2 HttpServletResponse (javax.servlet.http.HttpServletResponse)2 FileApiUrlBuilder (jetbrains.buildServer.server.rest.model.files.FileApiUrlBuilder)2 ArtifactElement (jetbrains.buildServer.web.artifacts.browser.ArtifactElement)2 Nullable (org.jetbrains.annotations.Nullable)2 Logger (com.intellij.openapi.diagnostic.Logger)1 ApiOperation (io.swagger.annotations.ApiOperation)1 BufferedOutputStream (java.io.BufferedOutputStream)1 File (java.io.File)1 OutputStream (java.io.OutputStream)1 ParseException (java.text.ParseException)1 java.util (java.util)1 Date (java.util.Date)1 Pattern (java.util.regex.Pattern)1 Collectors (java.util.stream.Collectors)1 WebApplicationException (javax.ws.rs.WebApplicationException)1 StreamingOutput (javax.ws.rs.core.StreamingOutput)1