You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by mc...@apache.org on 2016/01/06 22:20:57 UTC
[2/4] nifi git commit: NIFI-108: Built out 'skeleton' of the requests
so that web tier can be written against it
http://git-wip-us.apache.org/repos/asf/nifi/blob/b330fd16/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java
index 6741348..5831d6d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java
@@ -22,65 +22,79 @@ import com.wordnik.swagger.annotations.ApiParam;
import com.wordnik.swagger.annotations.ApiResponse;
import com.wordnik.swagger.annotations.ApiResponses;
import com.wordnik.swagger.annotations.Authorization;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import javax.servlet.http.HttpServletRequest;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.DefaultValue;
-import javax.ws.rs.FormParam;
-import javax.ws.rs.GET;
-import javax.ws.rs.HttpMethod;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.Status;
-
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.cluster.context.ClusterContext;
+import org.apache.nifi.cluster.context.ClusterContextThreadLocal;
+import org.apache.nifi.cluster.manager.exception.UnknownNodeException;
import org.apache.nifi.cluster.manager.impl.WebClusterManager;
+import org.apache.nifi.cluster.node.Node;
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.controller.queue.SortColumn;
+import org.apache.nifi.controller.queue.SortDirection;
+import org.apache.nifi.stream.io.StreamUtils;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.ConfigurationSnapshot;
+import org.apache.nifi.web.DownloadableContent;
import org.apache.nifi.web.IllegalClusterResourceRequestException;
import org.apache.nifi.web.NiFiServiceFacade;
import org.apache.nifi.web.Revision;
-import static org.apache.nifi.web.api.ApplicationResource.CLIENT_ID;
import org.apache.nifi.web.api.dto.ConnectableDTO;
import org.apache.nifi.web.api.dto.ConnectionDTO;
+import org.apache.nifi.web.api.dto.DropRequestDTO;
+import org.apache.nifi.web.api.dto.FlowFileDTO;
+import org.apache.nifi.web.api.dto.FlowFileSummaryDTO;
+import org.apache.nifi.web.api.dto.ListingRequestDTO;
import org.apache.nifi.web.api.dto.PositionDTO;
import org.apache.nifi.web.api.dto.RevisionDTO;
import org.apache.nifi.web.api.dto.status.StatusHistoryDTO;
import org.apache.nifi.web.api.entity.ConnectionEntity;
import org.apache.nifi.web.api.entity.ConnectionsEntity;
+import org.apache.nifi.web.api.entity.DropRequestEntity;
+import org.apache.nifi.web.api.entity.FlowFileEntity;
+import org.apache.nifi.web.api.entity.ListingRequestEntity;
import org.apache.nifi.web.api.entity.StatusHistoryEntity;
import org.apache.nifi.web.api.request.ClientIdParameter;
import org.apache.nifi.web.api.request.ConnectableTypeParameter;
import org.apache.nifi.web.api.request.IntegerParameter;
import org.apache.nifi.web.api.request.LongParameter;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.cluster.context.ClusterContext;
-import org.apache.nifi.cluster.context.ClusterContextThreadLocal;
-import org.apache.nifi.web.api.dto.DropRequestDTO;
-import org.apache.nifi.web.api.entity.DropRequestEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.prepost.PreAuthorize;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.StreamingOutput;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
/**
* RESTful endpoint for managing a Connection.
*/
@@ -95,7 +109,7 @@ public class ConnectionResource extends ApplicationResource {
private String groupId;
/**
- * Populate the uri's for the specified processors and their relationships.
+ * Populate the URIs for the specified connections.
*
* @param connections connections
* @return dtos
@@ -108,7 +122,10 @@ public class ConnectionResource extends ApplicationResource {
}
/**
- * Populate the uri's for the specified processor and its relationships.
+ * Populate the URIs for the specified connection.
+ *
+ * @param connection connection
+ * @return dto
*/
private ConnectionDTO populateRemainingConnectionContent(ConnectionDTO connection) {
// populate the remaining properties
@@ -117,6 +134,38 @@ public class ConnectionResource extends ApplicationResource {
}
/**
+ * Populate the URIs for the specified flowfile listing.
+ *
+ * @param connectionId connection
+ * @param flowFileListing flowfile listing
+ * @return dto
+ */
+ public ListingRequestDTO populateRemainingFlowFileListingContent(final String connectionId, final ListingRequestDTO flowFileListing) {
+ // uri of the listing
+ flowFileListing.setUri(generateResourceUri("controller", "process-groups", groupId, "connections", connectionId, "listing-requests", flowFileListing.getId()));
+
+ // uri of each flowfile
+ if (flowFileListing.getFlowFileSummaries() != null) {
+ for (FlowFileSummaryDTO flowFile : flowFileListing.getFlowFileSummaries()) {
+ populateRemainingFlowFileContent(connectionId, flowFile);
+ }
+ }
+ return flowFileListing;
+ }
+
+ /**
+ * Populate the URIs for the specified flowfile.
+ *
+ * @param connectionId the connection id
+ * @param flowFile the flowfile
+ * @return the dto
+ */
+ private FlowFileSummaryDTO populateRemainingFlowFileContent(final String connectionId, final FlowFileSummaryDTO flowFile) {
+ flowFile.setUri(generateResourceUri("controller", "process-groups", groupId, "connections", connectionId, "flowfiles", flowFile.getUuid()));
+ return flowFile;
+ }
+
+ /**
* Gets all the connections.
*
* @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
@@ -839,7 +888,7 @@ public class ConnectionResource extends ApplicationResource {
@ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.")
}
)
- public Response deleteRelationshipTarget(
+ public Response deleteConnection(
@Context HttpServletRequest httpServletRequest,
@ApiParam(
value = "The revision is used to verify the client is working with the latest version of the flow.",
@@ -892,7 +941,195 @@ public class ConnectionResource extends ApplicationResource {
}
/**
- * Drops the flowfiles in the queue of the specified connection.
+ * Gets the specified flowfile from the specified connection.
+ *
+ * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
+ * @param connectionId The connection id
+ * @param flowFileUuid The flowfile uuid
+ * @param clusterNodeId The cluster node id where the flowfile resides
+ * @return a flowFileDTO
+ */
+ @GET
+ @Consumes(MediaType.WILDCARD)
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @Path("/{connection-id}/flowfiles/{flowfile-uuid}")
+ @PreAuthorize("hasRole('ROLE_DFM')")
+ @ApiOperation(
+ value = "Gets a FlowFile from a Connection.",
+ authorizations = {
+ @Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
+ }
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."),
+ @ApiResponse(code = 401, message = "Client could not be authenticated."),
+ @ApiResponse(code = 403, message = "Client is not authorized to make this request."),
+ @ApiResponse(code = 404, message = "The specified resource could not be found."),
+ @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.")
+ }
+ )
+ public Response getFlowFile(
+ @ApiParam(
+ value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
+ required = false
+ )
+ @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+ @ApiParam(
+ value = "The connection id.",
+ required = true
+ )
+ @PathParam("connection-id") String connectionId,
+ @ApiParam(
+ value = "The flowfile uuid.",
+ required = true
+ )
+ @PathParam("flowfile-uuid") String flowFileUuid,
+ @ApiParam(
+ value = "The id of the node where the content exists if clustered.",
+ required = false
+ )
+ @QueryParam("clusterNodeId") String clusterNodeId) {
+
+ // replicate if cluster manager
+ if (properties.isClusterManager()) {
+ // determine where this request should be sent
+ if (clusterNodeId == null) {
+ throw new IllegalArgumentException("The id of the node in the cluster is required.");
+ } else {
+ // get the target node and ensure it exists
+ final Node targetNode = clusterManager.getNode(clusterNodeId);
+ if (targetNode == null) {
+ throw new UnknownNodeException("The specified cluster node does not exist.");
+ }
+
+ final Set<NodeIdentifier> targetNodes = new HashSet<>();
+ targetNodes.add(targetNode.getNodeId());
+
+ // replicate the request to the specific node
+ return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse();
+ }
+ }
+
+ // get the flowfile
+ final FlowFileDTO flowfileDto = serviceFacade.getFlowFile(groupId, connectionId, flowFileUuid);
+ populateRemainingFlowFileContent(connectionId, flowfileDto);
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(clientId.getClientId());
+
+ // create the response entity
+ final FlowFileEntity entity = new FlowFileEntity();
+ entity.setRevision(revision);
+ entity.setFlowFile(flowfileDto);
+
+ return generateOkResponse(entity).build();
+ }
+
+ /**
+ * Gets the content for the specified flowfile in the specified connection.
+ *
+ * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
+ * @param connectionId The connection id
+ * @param flowFileUuid The flowfile uuid
+ * @param clusterNodeId The cluster node id
+ * @return The content stream
+ */
+ @GET
+ @Consumes(MediaType.WILDCARD)
+ @Produces(MediaType.WILDCARD)
+ @Path("/{connection-id}/flowfiles/{flowfile-uuid}/content")
+ @PreAuthorize("hasRole('ROLE_DFM')")
+ @ApiOperation(
+ value = "Gets the content for a FlowFile in a Connection.",
+ authorizations = {
+ @Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
+ }
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."),
+ @ApiResponse(code = 401, message = "Client could not be authenticated."),
+ @ApiResponse(code = 403, message = "Client is not authorized to make this request."),
+ @ApiResponse(code = 404, message = "The specified resource could not be found."),
+ @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.")
+ }
+ )
+ public Response downloadFlowFileContent(
+ @ApiParam(
+ value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
+ required = false
+ )
+ @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+ @ApiParam(
+ value = "The connection id.",
+ required = true
+ )
+ @PathParam("connection-id") String connectionId,
+ @ApiParam(
+ value = "The flowfile uuid.",
+ required = true
+ )
+ @PathParam("flowfile-uuid") String flowFileUuid,
+ @ApiParam(
+ value = "The id of the node where the content exists if clustered.",
+ required = false
+ )
+ @QueryParam("clusterNodeId") String clusterNodeId) {
+
+ // replicate if cluster manager
+ if (properties.isClusterManager()) {
+ // determine where this request should be sent
+ if (clusterNodeId == null) {
+ throw new IllegalArgumentException("The id of the node in the cluster is required.");
+ } else {
+ // get the target node and ensure it exists
+ final Node targetNode = clusterManager.getNode(clusterNodeId);
+ if (targetNode == null) {
+ throw new UnknownNodeException("The specified cluster node does not exist.");
+ }
+
+ final Set<NodeIdentifier> targetNodes = new HashSet<>();
+ targetNodes.add(targetNode.getNodeId());
+
+ // replicate the request to the specific node
+ return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse();
+ }
+ }
+
+ // get the uri of the request
+ final String uri = generateResourceUri("controller", "process-groups", groupId, "connections", connectionId, "flowfiles", flowFileUuid, "content");
+
+ // get an input stream to the content
+ final DownloadableContent content = serviceFacade.getContent(groupId, connectionId, flowFileUuid, uri);
+
+ // generate a streaming response
+ final StreamingOutput response = new StreamingOutput() {
+ @Override
+ public void write(OutputStream output) throws IOException, WebApplicationException {
+ try (InputStream is = content.getContent()) {
+ // stream the content to the response
+ StreamUtils.copy(is, output);
+
+ // flush the response
+ output.flush();
+ }
+ }
+ };
+
+ // use the appropriate content type
+ String contentType = content.getType();
+ if (contentType == null) {
+ contentType = MediaType.APPLICATION_OCTET_STREAM;
+ }
+
+ return generateOkResponse(response).type(contentType).header("Content-Disposition", String.format("attachment; filename=\"%s\"", content.getFilename())).build();
+ }
+
+ /**
+ * Drops the flowfiles in the queue of the specified connection. This endpoint is DEPRECATED. Please use
+ * POST /nifi-api/controller/process-groups/{process-group-id}/connections/{connection-id}/drop-requests instead.
*
* @param httpServletRequest request
* @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
@@ -906,6 +1143,7 @@ public class ConnectionResource extends ApplicationResource {
@PreAuthorize("hasRole('ROLE_DFM')")
@ApiOperation(
value = "Drops the contents of the queue in this connection.",
+ notes = "This endpoint is DEPRECATED. Please use POST /nifi-api/controller/process-groups/{process-group-id}/connections/{connection-id}/drop-requests instead.",
response = DropRequestEntity.class,
authorizations = {
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
@@ -913,6 +1151,7 @@ public class ConnectionResource extends ApplicationResource {
)
@ApiResponses(
value = {
+ @ApiResponse(code = 202, message = "The request has been accepted. A HTTP response header will contain the URI where the response can be polled."),
@ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."),
@ApiResponse(code = 401, message = "Client could not be authenticated."),
@ApiResponse(code = 403, message = "Client is not authorized to make this request."),
@@ -920,6 +1159,7 @@ public class ConnectionResource extends ApplicationResource {
@ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.")
}
)
+ @Deprecated
public Response dropQueueContents(
@Context HttpServletRequest httpServletRequest,
@ApiParam(
@@ -933,6 +1173,319 @@ public class ConnectionResource extends ApplicationResource {
)
@PathParam("connection-id") String id) {
+ // defer to the new endpoint that references /drop-requests in the URI
+ return createDropRequest(httpServletRequest, clientId, id);
+ }
+
+ /**
+ * Creates a request to list the flowfiles in the queue of the specified connection.
+ *
+ * @param httpServletRequest request
+ * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
+ * @param id The id of the connection
+ * @return A listRequestEntity
+ */
+ @POST
+ @Consumes(MediaType.WILDCARD)
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @Path("/{connection-id}/listing-requests")
+ @PreAuthorize("hasRole('ROLE_DFM')")
+ @ApiOperation(
+ value = "Lists the contents of the queue in this connection.",
+ response = ListingRequestEntity.class,
+ authorizations = {
+ @Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
+ }
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = 202, message = "The request has been accepted. A HTTP response header will contain the URI where the response can be polled."),
+ @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."),
+ @ApiResponse(code = 401, message = "Client could not be authenticated."),
+ @ApiResponse(code = 403, message = "Client is not authorized to make this request."),
+ @ApiResponse(code = 404, message = "The specified resource could not be found."),
+ @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.")
+ }
+ )
+ public Response createFlowFileListing(
+ @Context HttpServletRequest httpServletRequest,
+ @ApiParam(
+ value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
+ required = false
+ )
+ @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+ @ApiParam(
+ value = "The connection id.",
+ required = true
+ )
+ @PathParam("connection-id") String id,
+ @ApiParam(
+ value = "The sort column.",
+ required = false,
+ defaultValue = "QUEUE_POSITION",
+ allowableValues = "QUEUE_POSITION, FLOWFILE_UUID, FILENAME, FLOWFILE_SIZE, QUEUED_DURATION, FLOWFILE_AGE, PENALIZATION"
+ )
+ @FormParam("sortColumn") String sortColumn,
+ @ApiParam(
+ value = "The sort direction.",
+ required = false,
+ defaultValue = "asc",
+ allowableValues = "asc, desc"
+ )
+ @FormParam("sortOrder") @DefaultValue("asc") String sortOrder) {
+
+ // parse the sort column
+ final SortColumn column;
+ if (sortColumn == null) {
+ column = SortColumn.QUEUE_POSITION;
+ } else {
+ try {
+ column = SortColumn.valueOf(sortColumn);
+ } catch (final IllegalArgumentException iae) {
+ throw new IllegalArgumentException(String.format("Sort Column: Value must be one of [%s]", StringUtils.join(SortColumn.values(), ", ")));
+ }
+ }
+
+ // normalize the sort order
+ if (!sortOrder.equalsIgnoreCase("asc") && !sortOrder.equalsIgnoreCase("desc")) {
+ throw new IllegalArgumentException("The sort order must be 'asc' or 'desc'.");
+ }
+
+ // replicate if cluster manager
+ if (properties.isClusterManager()) {
+ return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
+ }
+
+ // handle expects request (usually from the cluster manager)
+ final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER);
+ if (expects != null) {
+ serviceFacade.verifyListQueue(groupId, id);
+ return generateContinueResponse().build();
+ }
+
+ final SortDirection direction;
+ if (sortOrder.equalsIgnoreCase("asc")) {
+ direction = SortDirection.ASCENDING;
+ } else {
+ direction = SortDirection.DESCENDING;
+ }
+
+ // ensure the id is the same across the cluster
+ final String listingRequestId;
+ final ClusterContext clusterContext = ClusterContextThreadLocal.getContext();
+ if (clusterContext != null) {
+ listingRequestId = UUID.nameUUIDFromBytes(clusterContext.getIdGenerationSeed().getBytes(StandardCharsets.UTF_8)).toString();
+ } else {
+ listingRequestId = UUID.randomUUID().toString();
+ }
+
+ // submit the listing request
+ final ListingRequestDTO listingRequest = serviceFacade.createFlowFileListingRequest(groupId, id, listingRequestId, column, direction);
+ populateRemainingFlowFileListingContent(id, listingRequest);
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(clientId.getClientId());
+
+ // create the response entity
+ final ListingRequestEntity entity = new ListingRequestEntity();
+ entity.setRevision(revision);
+ entity.setListingRequest(listingRequest);
+
+ // generate the URI where the response will be
+ final URI location = URI.create(listingRequest.getUri());
+ return Response.status(Status.ACCEPTED).location(location).entity(entity).build();
+ }
+
+ /**
+ * Checks the status of an outstanding listing request.
+ *
+ * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
+ * @param connectionId The id of the connection
+ * @param listingRequestId The id of the drop request
+ * @return A dropRequestEntity
+ */
+ @GET
+ @Consumes(MediaType.WILDCARD)
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @Path("/{connection-id}/listing-requests/{listing-request-id}")
+ @PreAuthorize("hasRole('ROLE_DFM')")
+ @ApiOperation(
+ value = "Gets the current status of a listing request for the specified connection.",
+ response = ListingRequestEntity.class,
+ authorizations = {
+ @Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
+ }
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."),
+ @ApiResponse(code = 401, message = "Client could not be authenticated."),
+ @ApiResponse(code = 403, message = "Client is not authorized to make this request."),
+ @ApiResponse(code = 404, message = "The specified resource could not be found."),
+ @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.")
+ }
+ )
+ public Response getListingRequest(
+ @ApiParam(
+ value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
+ required = false
+ )
+ @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+ @ApiParam(
+ value = "The connection id.",
+ required = true
+ )
+ @PathParam("connection-id") String connectionId,
+ @ApiParam(
+ value = "The listing request id.",
+ required = true
+ )
+ @PathParam("listing-request-id") String listingRequestId) {
+
+ // replicate if cluster manager
+ if (properties.isClusterManager()) {
+ return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
+ }
+
+ // get the listing request
+ final ListingRequestDTO listingRequest = serviceFacade.getFlowFileListingRequest(groupId, connectionId, listingRequestId);
+ populateRemainingFlowFileListingContent(connectionId, listingRequest);
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(clientId.getClientId());
+
+ // create the response entity
+ final ListingRequestEntity entity = new ListingRequestEntity();
+ entity.setRevision(revision);
+ entity.setListingRequest(listingRequest);
+
+ return generateOkResponse(entity).build();
+ }
+
+ /**
+ * Deletes the specified listing request.
+ *
+ * @param httpServletRequest request
+ * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
+ * @param connectionId The connection id
+ * @param listingRequestId The drop request id
+ * @return A dropRequestEntity
+ */
+ @DELETE
+ @Consumes(MediaType.WILDCARD)
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @Path("/{connection-id}/listing-requests/{listing-request-id}")
+ @ApiOperation(
+ value = "Cancels and/or removes a request to list the contents of this connection.",
+ response = DropRequestEntity.class,
+ authorizations = {
+ @Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
+ }
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."),
+ @ApiResponse(code = 401, message = "Client could not be authenticated."),
+ @ApiResponse(code = 403, message = "Client is not authorized to make this request."),
+ @ApiResponse(code = 404, message = "The specified resource could not be found."),
+ @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.")
+ }
+ )
+ public Response deleteListingRequest(
+ @Context HttpServletRequest httpServletRequest,
+ @ApiParam(
+ value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
+ required = false
+ )
+ @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+ @ApiParam(
+ value = "The connection id.",
+ required = true
+ )
+ @PathParam("connection-id") String connectionId,
+ @ApiParam(
+ value = "The listing request id.",
+ required = true
+ )
+ @PathParam("listing-request-id") String listingRequestId) {
+
+ // replicate if cluster manager
+ if (properties.isClusterManager()) {
+ return clusterManager.applyRequest(HttpMethod.DELETE, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
+ }
+
+ // handle expects request (usually from the cluster manager)
+ final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER);
+ if (expects != null) {
+ return generateContinueResponse().build();
+ }
+
+ // delete the listing request
+ final ListingRequestDTO listingRequest = serviceFacade.deleteFlowFileListingRequest(groupId, connectionId, listingRequestId);
+
+ // prune the results as they were already received when the listing completed
+ listingRequest.setFlowFileSummaries(null);
+
+ // populate remaining content
+ populateRemainingFlowFileListingContent(connectionId, listingRequest);
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(clientId.getClientId());
+
+ // create the response entity
+ final ListingRequestEntity entity = new ListingRequestEntity();
+ entity.setRevision(revision);
+ entity.setListingRequest(listingRequest);
+
+ return generateOkResponse(entity).build();
+ }
+
+ /**
+ * Creates a request to delete the flowfiles in the queue of the specified connection.
+ *
+ * @param httpServletRequest request
+ * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
+ * @param id The id of the connection
+ * @return A dropRequestEntity
+ */
+ @POST
+ @Consumes(MediaType.WILDCARD)
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @Path("/{connection-id}/drop-requests")
+ @PreAuthorize("hasRole('ROLE_DFM')")
+ @ApiOperation(
+ value = "Creates a request to drop the contents of the queue in this connection.",
+ response = DropRequestEntity.class,
+ authorizations = {
+ @Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
+ }
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = 202, message = "The request has been accepted. A HTTP response header will contain the URI where the response can be polled."),
+ @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."),
+ @ApiResponse(code = 401, message = "Client could not be authenticated."),
+ @ApiResponse(code = 403, message = "Client is not authorized to make this request."),
+ @ApiResponse(code = 404, message = "The specified resource could not be found."),
+ @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.")
+ }
+ )
+ public Response createDropRequest(
+ @Context HttpServletRequest httpServletRequest,
+ @ApiParam(
+ value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
+ required = false
+ )
+ @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+ @ApiParam(
+ value = "The connection id.",
+ required = true
+ )
+ @PathParam("connection-id") String id) {
+
// replicate if cluster manager
if (properties.isClusterManager()) {
return clusterManager.applyRequest(HttpMethod.DELETE, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
@@ -968,11 +1521,7 @@ public class ConnectionResource extends ApplicationResource {
// generate the URI where the response will be
final URI location = URI.create(dropRequest.getUri());
- if (dropRequest.isFinished()) {
- return generateCreatedResponse(location, entity).build();
- } else {
- return Response.status(Status.ACCEPTED).location(location).entity(entity).build();
- }
+ return Response.status(Status.ACCEPTED).location(location).entity(entity).build();
}
/**
@@ -1057,7 +1606,7 @@ public class ConnectionResource extends ApplicationResource {
@Path("/{connection-id}/drop-requests/{drop-request-id}")
@PreAuthorize("hasRole('ROLE_DFM')")
@ApiOperation(
- value = "Cancels and/or removes a request drop of the contents in this connection.",
+ value = "Cancels and/or removes a request to drop the contents of this connection.",
response = DropRequestEntity.class,
authorizations = {
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
http://git-wip-us.apache.org/repos/asf/nifi/blob/b330fd16/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
index a2619e6..6433e9f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
@@ -75,6 +75,12 @@ import org.apache.nifi.controller.ProcessorNode;
import org.apache.nifi.controller.Snippet;
import org.apache.nifi.controller.Template;
import org.apache.nifi.controller.label.Label;
+import org.apache.nifi.controller.queue.FlowFileSummary;
+import org.apache.nifi.controller.queue.ListFlowFileState;
+import org.apache.nifi.controller.queue.ListFlowFileStatus;
+import org.apache.nifi.controller.repository.FlowFileRecord;
+import org.apache.nifi.controller.repository.claim.ContentClaim;
+import org.apache.nifi.controller.repository.claim.ResourceClaim;
import org.apache.nifi.controller.status.ConnectionStatus;
import org.apache.nifi.controller.status.PortStatus;
import org.apache.nifi.controller.status.ProcessGroupStatus;
@@ -84,6 +90,7 @@ import org.apache.nifi.diagnostics.GarbageCollection;
import org.apache.nifi.diagnostics.StorageUsage;
import org.apache.nifi.diagnostics.SystemDiagnostics;
import org.apache.nifi.flowfile.FlowFilePrioritizer;
+import org.apache.nifi.flowfile.attributes.CoreAttributes;
import org.apache.nifi.groups.ProcessGroup;
import org.apache.nifi.groups.ProcessGroupCounts;
import org.apache.nifi.groups.RemoteProcessGroup;
@@ -345,6 +352,113 @@ public final class DtoFactory {
return dto;
}
+ private boolean isListingRequestComplete(final ListFlowFileState state) {
+ return ListFlowFileState.COMPLETE.equals(state) || ListFlowFileState.CANCELED.equals(state) || ListFlowFileState.FAILURE.equals(state);
+ }
+
+ private QueueSizeDTO createQueueSizeDTO(final QueueSize queueSize) {
+ final QueueSizeDTO dto = new QueueSizeDTO();
+ dto.setByteCount(queueSize.getByteCount());
+ dto.setObjectCount(queueSize.getObjectCount());
+ return dto;
+ }
+
+ /**
+ * Creates a ListingRequestDTO from the specified ListFlowFileStatus.
+ *
+ * @param listingRequest listingRequest
+ * @return dto
+ */
+ public ListingRequestDTO createListingRequestDTO(final ListFlowFileStatus listingRequest) {
+ final ListingRequestDTO dto = new ListingRequestDTO();
+ dto.setId(listingRequest.getRequestIdentifier());
+ dto.setSubmissionTime(new Date(listingRequest.getRequestSubmissionTime()));
+ dto.setLastUpdated(new Date(listingRequest.getLastUpdated()));
+ dto.setState(listingRequest.getState().toString());
+ dto.setFailureReason(listingRequest.getFailureReason());
+ dto.setFinished(isListingRequestComplete(listingRequest.getState()));
+ dto.setMaxResults(listingRequest.getMaxResults());
+ dto.setSortColumn(listingRequest.getSortColumn().name());
+ dto.setSortDirection(listingRequest.getSortDirection().name());
+ dto.setTotalStepCount(listingRequest.getTotalStepCount());
+ dto.setCompletedStepCount(listingRequest.getCompletedStepCount());
+ dto.setPercentCompleted(listingRequest.getCompletionPercentage());
+
+ dto.setQueueSize(createQueueSizeDTO(listingRequest.getQueueSize()));
+
+ if (isListingRequestComplete(listingRequest.getState())) {
+ final List<FlowFileSummary> flowFileSummaries = listingRequest.getFlowFileSummaries();
+ if (flowFileSummaries != null) {
+ final Date now = new Date();
+ final List<FlowFileSummaryDTO> summaryDtos = new ArrayList<>(flowFileSummaries.size());
+ for (final FlowFileSummary summary : flowFileSummaries) {
+ summaryDtos.add(createFlowFileSummaryDTO(summary, now));
+ }
+ dto.setFlowFileSummaries(summaryDtos);
+ }
+ }
+
+ return dto;
+ }
+
+ /**
+ * Creates a FlowFileSummaryDTO from the specified FlowFileSummary.
+ *
+ * @param summary summary
+ * @return dto
+ */
+ public FlowFileSummaryDTO createFlowFileSummaryDTO(final FlowFileSummary summary, final Date now) {
+ final FlowFileSummaryDTO dto = new FlowFileSummaryDTO();
+ dto.setUuid(summary.getUuid());
+ dto.setFilename(summary.getFilename());
+ dto.setPenalized(summary.isPenalized());
+ dto.setPosition(summary.getPosition());
+ dto.setSize(summary.getSize());
+
+ final long queuedDuration = now.getTime() - summary.getLastQueuedTime();
+ dto.setQueuedDuration(queuedDuration);
+
+ final long age = now.getTime() - summary.getLineageStartDate();
+ dto.setLineageDuration(age);
+
+ return dto;
+ }
+
+ /**
+ * Creates a FlowFileDTO from the specified FlowFileRecord.
+ *
+ * @param record record
+ * @return dto
+ */
+ public FlowFileDTO createFlowFileDTO(final FlowFileRecord record) {
+ final Date now = new Date();
+ final FlowFileDTO dto = new FlowFileDTO();
+ dto.setUuid(record.getAttribute(CoreAttributes.UUID.key()));
+ dto.setFilename(record.getAttribute(CoreAttributes.FILENAME.key()));
+ dto.setPenalized(record.isPenalized());
+ dto.setSize(record.getSize());
+ dto.setAttributes(record.getAttributes());
+
+ final long queuedDuration = now.getTime() - record.getLastQueueDate();
+ dto.setQueuedDuration(queuedDuration);
+
+ final long age = now.getTime() - record.getLineageStartDate();
+ dto.setLineageDuration(age);
+
+ final ContentClaim contentClaim = record.getContentClaim();
+ if (contentClaim != null) {
+ final ResourceClaim resourceClaim = contentClaim.getResourceClaim();
+ dto.setContentClaimSection(resourceClaim.getSection());
+ dto.setContentClaimContainer(resourceClaim.getContainer());
+ dto.setContentClaimIdentifier(resourceClaim.getId());
+ dto.setContentClaimOffset(contentClaim.getOffset());
+ dto.setContentClaimFileSizeBytes(contentClaim.getLength());
+ dto.setContentClaimFileSize(FormatUtils.formatDataSize(contentClaim.getLength()));
+ }
+
+ return dto;
+ }
+
/**
* Creates a ConnectionDTO from the specified Connection.
*
http://git-wip-us.apache.org/repos/asf/nifi/blob/b330fd16/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ConnectionDAO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ConnectionDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ConnectionDAO.java
index 2be4403..932cc23 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ConnectionDAO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ConnectionDAO.java
@@ -19,6 +19,11 @@ package org.apache.nifi.web.dao;
import java.util.Set;
import org.apache.nifi.connectable.Connection;
import org.apache.nifi.controller.queue.DropFlowFileStatus;
+import org.apache.nifi.controller.queue.ListFlowFileStatus;
+import org.apache.nifi.controller.queue.SortColumn;
+import org.apache.nifi.controller.queue.SortDirection;
+import org.apache.nifi.controller.repository.FlowFileRecord;
+import org.apache.nifi.web.DownloadableContent;
import org.apache.nifi.web.api.dto.ConnectionDTO;
public interface ConnectionDAO {
@@ -43,6 +48,26 @@ public interface ConnectionDAO {
DropFlowFileStatus getFlowFileDropRequest(String groupId, String id, String dropRequestId);
/**
+ * Gets the specified flowfile listing request.
+ *
+ * @param groupId group id
+ * @param id connection id
+ * @param listingRequestId The listing request id
+ * @return The listing request status
+ */
+ ListFlowFileStatus getFlowFileListingRequest(String groupId, String id, String listingRequestId);
+
+ /**
+ * Gets the specified flowfile in the specified connection.
+ *
+ * @param groupId group id
+ * @param id connection id
+ * @param flowFileUuid the flowfile uuid
+ * @return The flowfile
+ */
+ FlowFileRecord getFlowFile(String groupId, String id, String flowFileUuid);
+
+ /**
* Gets the connections for the specified source processor.
*
* @param groupId group id
@@ -85,7 +110,27 @@ public interface ConnectionDAO {
* @param dropRequestId drop request id
* @return The drop request status
*/
- DropFlowFileStatus createFileFlowDropRequest(String groupId, String id, String dropRequestId);
+ DropFlowFileStatus createFlowFileDropRequest(String groupId, String id, String dropRequestId);
+
+ /**
+ * Creates a new flow file listing request.
+ *
+ * @param groupId group id
+ * @param id connection id
+ * @param listingRequestId listing request id
+ * @param column sort column
+ * @param direction sort direction
+ * @return The listing request status
+ */
+ ListFlowFileStatus createFlowFileListingRequest(String groupId, String id, String listingRequestId, SortColumn column, SortDirection direction);
+
+ /**
+ * Verifies the listing can be processed.
+ *
+ * @param groupId group id
+ * @param id connection id
+ */
+ void verifyList(String groupId, String id);
/**
* Verifies the create request can be processed.
@@ -137,4 +182,25 @@ public interface ConnectionDAO {
* @return The drop request
*/
DropFlowFileStatus deleteFlowFileDropRequest(String groupId, String id, String dropRequestId);
+
+ /**
+ * Deletes the specified flow file listing request.
+ *
+ * @param groupId group id
+ * @param id connection id
+ * @param listingRequestId The listing request id
+ * @return The listing request status
+ */
+ ListFlowFileStatus deleteFlowFileListingRequest(String groupId, String id, String listingRequestId);
+
+ /**
+ * Gets the content for the specified flowfile in the specified connection.
+ *
+ * @param groupId group id
+ * @param id connection id
+ * @param flowfileUuid flowfile uuid
+ * @param requestUri request uri
+ * @return The downloadable content
+ */
+ DownloadableContent getContent(String groupId, String id, String flowfileUuid, String requestUri);
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/b330fd16/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java
index 565e5af..d5fb713 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java
@@ -16,15 +16,20 @@
*/
package org.apache.nifi.web.dao.impl;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import javax.ws.rs.WebApplicationException;
+import org.apache.nifi.admin.service.UserService;
+import org.apache.nifi.authorization.DownloadAuthorization;
import org.apache.nifi.connectable.Connectable;
import org.apache.nifi.connectable.ConnectableType;
import org.apache.nifi.connectable.Connection;
@@ -34,6 +39,12 @@ import org.apache.nifi.controller.ProcessorNode;
import org.apache.nifi.controller.exception.ValidationException;
import org.apache.nifi.controller.queue.DropFlowFileStatus;
import org.apache.nifi.controller.queue.FlowFileQueue;
+import org.apache.nifi.controller.queue.ListFlowFileStatus;
+import org.apache.nifi.controller.queue.SortColumn;
+import org.apache.nifi.controller.queue.SortDirection;
+import org.apache.nifi.controller.repository.ContentNotFoundException;
+import org.apache.nifi.controller.repository.FlowFileRecord;
+import org.apache.nifi.flowfile.attributes.CoreAttributes;
import org.apache.nifi.groups.ProcessGroup;
import org.apache.nifi.groups.RemoteProcessGroup;
import org.apache.nifi.flowfile.FlowFilePrioritizer;
@@ -41,16 +52,24 @@ import org.apache.nifi.processor.Relationship;
import org.apache.nifi.remote.RemoteGroupPort;
import org.apache.nifi.user.NiFiUser;
import org.apache.nifi.util.FormatUtils;
+import org.apache.nifi.web.DownloadableContent;
import org.apache.nifi.web.ResourceNotFoundException;
import org.apache.nifi.web.api.dto.ConnectableDTO;
import org.apache.nifi.web.api.dto.ConnectionDTO;
import org.apache.nifi.web.api.dto.PositionDTO;
import org.apache.nifi.web.dao.ConnectionDAO;
+import org.apache.nifi.web.security.ProxiedEntitiesUtils;
import org.apache.nifi.web.security.user.NiFiUserUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.access.AccessDeniedException;
public class StandardConnectionDAO extends ComponentDAO implements ConnectionDAO {
+ private static final Logger logger = LoggerFactory.getLogger(StandardConnectionDAO.class);
+
private FlowController flowController;
+ private UserService userService;
private Connection locateConnection(final String groupId, final String id) {
return locateConnection(locateProcessGroup(flowController, groupId), id);
@@ -87,6 +106,37 @@ public class StandardConnectionDAO extends ComponentDAO implements ConnectionDAO
}
@Override
+ public ListFlowFileStatus getFlowFileListingRequest(String groupId, String connectionId, String listingRequestId) {
+ final Connection connection = locateConnection(groupId, connectionId);
+ final FlowFileQueue queue = connection.getFlowFileQueue();
+
+ final ListFlowFileStatus listRequest = queue.getListFlowFileStatus(listingRequestId);
+ if (listRequest == null) {
+ throw new ResourceNotFoundException(String.format("Unable to find listing request with id '%s'.", listingRequestId));
+ }
+
+ return listRequest;
+ }
+
+ @Override
+ public FlowFileRecord getFlowFile(String groupId, String id, String flowFileUuid) {
+ try {
+ final Connection connection = locateConnection(groupId, id);
+ final FlowFileQueue queue = connection.getFlowFileQueue();
+ final FlowFileRecord flowFile = queue.getFlowFile(flowFileUuid);
+
+ if (flowFile == null) {
+ throw new ResourceNotFoundException(String.format("Unable to find FlowFile '%s' in Connection '%s'.", flowFileUuid, id));
+ }
+
+ return flowFile;
+ } catch (final IOException ioe) {
+ logger.error(String.format("Unable to get the flowfile (%s) at this time.", flowFileUuid), ioe);
+ throw new IllegalStateException("Unable to get the FlowFile at this time.");
+ }
+ }
+
+ @Override
public Set<Connection> getConnectionsForSource(final String groupId, final String processorId) {
final Set<Connection> connections = new HashSet<>(getConnections(groupId));
for (final Iterator<Connection> connectionIter = connections.iterator(); connectionIter.hasNext();) {
@@ -312,7 +362,7 @@ public class StandardConnectionDAO extends ComponentDAO implements ConnectionDAO
}
@Override
- public DropFlowFileStatus createFileFlowDropRequest(String groupId, String id, String dropRequestId) {
+ public DropFlowFileStatus createFlowFileDropRequest(String groupId, String id, String dropRequestId) {
final Connection connection = locateConnection(groupId, id);
final FlowFileQueue queue = connection.getFlowFileQueue();
@@ -325,6 +375,17 @@ public class StandardConnectionDAO extends ComponentDAO implements ConnectionDAO
}
@Override
+ public ListFlowFileStatus createFlowFileListingRequest(String groupId, String id, String listingRequestId, SortColumn column, SortDirection direction) {
+ final Connection connection = locateConnection(groupId, id);
+ final FlowFileQueue queue = connection.getFlowFileQueue();
+
+ // ensure we can list
+ verifyList(queue);
+
+ return queue.listFlowFiles(listingRequestId, 100, column, direction);
+ }
+
+ @Override
public void verifyCreate(String groupId, ConnectionDTO connectionDTO) {
// validate the incoming request
final List<String> validationErrors = validateProposedConfiguration(groupId, connectionDTO);
@@ -335,6 +396,17 @@ public class StandardConnectionDAO extends ComponentDAO implements ConnectionDAO
}
}
+ private void verifyList(final FlowFileQueue queue) {
+ queue.verifyCanList();
+ }
+
+ @Override
+ public void verifyList(String groupId, String id) {
+ final Connection connection = locateConnection(groupId, id);
+ final FlowFileQueue queue = connection.getFlowFileQueue();
+ verifyList(queue);
+ }
+
@Override
public void verifyUpdate(String groupId, ConnectionDTO connectionDTO) {
final ProcessGroup group = locateProcessGroup(flowController, groupId);
@@ -508,8 +580,71 @@ public class StandardConnectionDAO extends ComponentDAO implements ConnectionDAO
return dropFlowFileStatus;
}
+ @Override
+ public ListFlowFileStatus deleteFlowFileListingRequest(String groupId, String connectionId, String listingRequestId) {
+ final Connection connection = locateConnection(groupId, connectionId);
+ final FlowFileQueue queue = connection.getFlowFileQueue();
+
+ final ListFlowFileStatus listFlowFileStatus = queue.cancelListFlowFileRequest(listingRequestId);
+ if (listFlowFileStatus == null) {
+ throw new ResourceNotFoundException(String.format("Unable to find listing request with id '%s'.", listingRequestId));
+ }
+
+ return listFlowFileStatus;
+ }
+
+ @Override
+ public DownloadableContent getContent(String groupId, String id, String flowFileUuid, String requestUri) {
+ try {
+ final NiFiUser user = NiFiUserUtils.getNiFiUser();
+ if (user == null) {
+ throw new WebApplicationException(new Throwable("Unable to access details for current user."));
+ }
+
+ final Connection connection = locateConnection(groupId, id);
+ final FlowFileQueue queue = connection.getFlowFileQueue();
+ final FlowFileRecord flowFile = queue.getFlowFile(flowFileUuid);
+
+ if (flowFile == null) {
+ throw new ResourceNotFoundException(String.format("Unable to find FlowFile '%s' in Connection '%s'.", flowFileUuid, id));
+ }
+
+ // calculate the dn chain
+ final List<String> dnChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(user);
+
+ // ensure the users in this chain are allowed to download this content
+ final Map<String, String> attributes = flowFile.getAttributes();
+ final DownloadAuthorization downloadAuthorization = userService.authorizeDownload(dnChain, attributes);
+ if (!downloadAuthorization.isApproved()) {
+ throw new AccessDeniedException(downloadAuthorization.getExplanation());
+ }
+
+ // get the filename and fall back to the identifier (should never happen)
+ String filename = attributes.get(CoreAttributes.FILENAME.key());
+ if (filename == null) {
+ filename = flowFileUuid;
+ }
+
+ // get the mime-type
+ final String type = attributes.get(CoreAttributes.MIME_TYPE.key());
+
+ // get the content
+ final InputStream content = flowController.getContent(flowFile, user.getIdentity(), requestUri);
+ return new DownloadableContent(filename, type, content);
+ } catch (final ContentNotFoundException cnfe) {
+ throw new ResourceNotFoundException("Unable to find the specified content.");
+ } catch (final IOException ioe) {
+ logger.error(String.format("Unable to get the content for flowfile (%s) at this time.", flowFileUuid), ioe);
+ throw new IllegalStateException("Unable to get the content at this time.");
+ }
+ }
+
/* setters */
public void setFlowController(final FlowController flowController) {
this.flowController = flowController;
}
+
+ public void setUserService(UserService userService) {
+ this.userService = userService;
+ }
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/b330fd16/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
index 9f3d2f5..406f38c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
@@ -76,6 +76,7 @@
</bean>
<bean id="connectionDAO" class="org.apache.nifi.web.dao.impl.StandardConnectionDAO">
<property name="flowController" ref="flowController"/>
+ <property name="userService" ref="userService"/>
</bean>
<bean id="processorDAO" class="org.apache.nifi.web.dao.impl.StandardProcessorDAO">
<property name="flowController" ref="flowController"/>
http://git-wip-us.apache.org/repos/asf/nifi/blob/b330fd16/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
index 35ad4a2..8e87131 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
@@ -280,6 +280,7 @@
<include>${staging.dir}/js/nf/canvas/nf-snippet.js</include>
<include>${staging.dir}/js/nf/canvas/nf-canvas-toolbox.js</include>
<include>${staging.dir}/js/nf/canvas/nf-custom-ui.js</include>
+ <include>${staging.dir}/js/nf/canvas/nf-queue-listing.js</include>
<include>${staging.dir}/js/nf/canvas/nf-controller-service.js</include>
<include>${staging.dir}/js/nf/canvas/nf-reporting-task.js</include>
<include>${staging.dir}/js/nf/canvas/nf-processor-configuration.js</include>
@@ -445,6 +446,7 @@
<include>${staging.dir}/css/remote-process-group-configuration.css</include>
<include>${staging.dir}/css/port-configuration.css</include>
<include>${staging.dir}/css/port-details.css</include>
+ <include>${staging.dir}/css/queue-listing.css</include>
<include>${staging.dir}/css/label-configuration.css</include>
<include>${staging.dir}/css/connection-configuration.css</include>
<include>${staging.dir}/css/connection-details.css</include>
http://git-wip-us.apache.org/repos/asf/nifi/blob/b330fd16/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
index bf61846..43e60f4 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
@@ -21,6 +21,7 @@ nf.canvas.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js?
<script type="text/javascript" src="js/nf/nf-shell.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-snippet.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/canvas/nf-queue-listing.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-canvas-toolbox.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-custom-ui.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-controller-service.js?${project.version}"></script>\n\
http://git-wip-us.apache.org/repos/asf/nifi/blob/b330fd16/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
index f9970df..5a9eab7 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
@@ -119,9 +119,13 @@
<jsp:include page="/WEB-INF/partials/canvas/label-configuration.jsp"/>
<jsp:include page="/WEB-INF/partials/canvas/connection-configuration.jsp"/>
<jsp:include page="/WEB-INF/partials/canvas/drop-request-status-dialog.jsp"/>
+ <jsp:include page="/WEB-INF/partials/canvas/flowfile-details-dialog.jsp"/>
+ <jsp:include page="/WEB-INF/partials/canvas/listing-request-status-dialog.jsp"/>
+ <jsp:include page="/WEB-INF/partials/canvas/queue-listing.jsp"/>
<jsp:include page="/WEB-INF/partials/connection-details.jsp"/>
<div id="faded-background"></div>
<div id="glass-pane"></div>
<div id="context-menu" class="unselectable"></div>
+ <span id="nifi-content-viewer-url" class="hidden"></span>
</body>
</html>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi/blob/b330fd16/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/flowfile-details-dialog.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/flowfile-details-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/flowfile-details-dialog.jsp
new file mode 100644
index 0000000..cead866
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/flowfile-details-dialog.jsp
@@ -0,0 +1,114 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="flowfile-details-dialog">
+ <div id="flowfile-details-dialog-content">
+ <div id="flowfile-details-tabs"></div>
+ <div id="flowfile-details-tabs-content">
+ <div id="flowfile-details-tab-content" class="details-tab">
+ <span id="flowfile-uri" class="hidden"></span>
+ <span id="flowfile-cluster-node-id" class="hidden"></span>
+ <div class="settings-left">
+ <div id="flowfile-details">
+ <div class="flowfile-header">FlowFile Details</div>
+ <div class="flowfile-detail">
+ <div class="detail-name">UUID</div>
+ <div id="flowfile-uuid" class="detail-value"></div>
+ <div class="clear"></div>
+ </div>
+ <div class="flowfile-detail">
+ <div class="detail-name">Filename</div>
+ <div id="flowfile-filename" class="detail-value"></div>
+ <div class="clear"></div>
+ </div>
+ <div class="flowfile-detail">
+ <div class="detail-name">File Size</div>
+ <div id="flowfile-file-size" class="detail-value"></div>
+ <div class="clear"></div>
+ </div>
+ <div class="flowfile-detail">
+ <div class="detail-name">Queue Position</div>
+ <div id="flowfile-queue-position" class="detail-value"></div>
+ <div class="clear"></div>
+ </div>
+ <div class="flowfile-detail">
+ <div class="detail-name">Queued Duration</div>
+ <div id="flowfile-queued-duration" class="detail-value"></div>
+ <div class="clear"></div>
+ </div>
+ <div class="flowfile-detail">
+ <div class="detail-name">Lineage Duration</div>
+ <div id="flowfile-lineage-duration" class="detail-value"></div>
+ <div class="clear"></div>
+ </div>
+ <div class="flowfile-detail">
+ <div class="detail-name">Penalized</div>
+ <div id="flowfile-penalized" class="detail-value"></div>
+ <div class="clear"></div>
+ </div>
+ <div id="additional-flowfile-details"></div>
+ </div>
+ </div>
+ <div class="spacer"> </div>
+ <div class="settings-right">
+ <div id="flowfile-content-details" class="content-details">
+ <div class="flowfile-header">Content Claim</div>
+ <div class="flowfile-detail">
+ <div class="content-detail-name">Container</div>
+ <div id="content-container" class="content-detail-value"></div>
+ <div class="clear"></div>
+ </div>
+ <div class="flowfile-detail">
+ <div class="content-detail-name">Section</div>
+ <div id="content-section" class="content-detail-value"></div>
+ <div class="clear"></div>
+ </div>
+ <div class="flowfile-detail">
+ <div class="content-detail-name">Identifier</div>
+ <div id="content-identifier" class="content-detail-value"></div>
+ <div class="clear"></div>
+ </div>
+ <div class="flowfile-detail">
+ <div class="content-detail-name">Offset</div>
+ <div id="content-offset" class="content-detail-value"></div>
+ <div class="clear"></div>
+ </div>
+ <div class="flowfile-detail">
+ <div class="content-detail-name">Size</div>
+ <div id="content-size" class="content-detail-value"></div>
+ <div id="content-bytes" class="content-detail-value hidden"></div>
+ <div class="clear"></div>
+ </div>
+ <div class="flowfile-detail">
+ <div id="content-download" class="button">Download</div>
+ <div id="content-view" class="button hidden">View</div>
+ <div class="clear"></div>
+ </div>
+ </div>
+ </div>
+ <div class="clear"></div>
+ </div>
+ <div id="flowfile-attributes-tab-content" class="details-tab">
+ <div id="flowfile-attributes-details">
+ <div id="flowfile-attributes-header" class="flowfile-header">Attribute Values</div>
+ <div class="clear"></div>
+ <div id="flowfile-attributes-container"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/nifi/blob/b330fd16/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/listing-request-status-dialog.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/listing-request-status-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/listing-request-status-dialog.jsp
new file mode 100644
index 0000000..3320388
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/listing-request-status-dialog.jsp
@@ -0,0 +1,29 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="listing-request-status-dialog">
+ <div class="dialog-content">
+ <div class="setting">
+ <div class="setting-field">
+ <div id="listing-request-status-message"></div>
+ </div>
+ <div class="setting-field">
+ <div id="listing-request-percent-complete"></div>
+ </div>
+ </div>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/nifi/blob/b330fd16/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/queue-listing.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/queue-listing.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/queue-listing.jsp
new file mode 100644
index 0000000..0d8435a
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/queue-listing.jsp
@@ -0,0 +1,29 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="queue-listing-container" class="hidden">
+ <div id="queue-listing-header-container">
+ <div id="queue-listing-header-text"></div>
+ </div>
+ <div id="queue-listing-stats-container">
+ <div id="queue-listing-stats">
+ Displaying <span id="displayed-flowfiles"></span> of <span id="total-flowfiles-count"></span> (<span id="total-flowfiles-size"></span>)
+ </div>
+ <div id="queue-listing-loading-container" class="loading-container"></div>
+ </div>
+ <div id="queue-listing-table"></div>
+</div>
http://git-wip-us.apache.org/repos/asf/nifi/blob/b330fd16/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css
index abb5ebd..2df31d6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css
@@ -19,6 +19,7 @@
@import url(processor-details.css);
@import url(process-group-configuration.css);
@import url(process-group-details.css);
+@import url(queue-listing.css);
@import url(remote-process-group-configuration.css);
@import url(controller-service.css);
@import url(reporting-task.css);
http://git-wip-us.apache.org/repos/asf/nifi/blob/b330fd16/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
index 9f4fefb..86d52fe 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
@@ -276,10 +276,6 @@ div.go-to-link {
z-index: 1301;
}
-#drop-request-status-message {
-
-}
-
#drop-request-percent-complete {
margin-top: 10px;
width: 378px;
@@ -290,6 +286,23 @@ div.go-to-link {
border-radius: 0;
}
+#listing-request-status-dialog {
+ display: none;
+ width: 400px;
+ height: 125px;
+ z-index: 1301;
+}
+
+#listing-request-percent-complete {
+ margin-top: 10px;
+ width: 378px;
+ border-radius: 0;
+}
+
+#listing-request-percent-complete .ui-progressbar-value {
+ border-radius: 0;
+}
+
div.progress-label {
color: #000000;
display: block;
http://git-wip-us.apache.org/repos/asf/nifi/blob/b330fd16/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/queue-listing.css
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/queue-listing.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/queue-listing.css
new file mode 100644
index 0000000..689ceaf
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/queue-listing.css
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+
+/*
+ Queue listing styles
+*/
+
+#queue-listing-container {
+ position: absolute;
+ top: 0px;
+ bottom: 0px;
+ left: 0px;
+ right: 0px;
+}
+
+#queue-listing-header-container {
+ height: 35px;
+ margin-top: 20px;
+ margin-left: 20px;
+ margin-right: 20px;
+}
+
+#queue-listing-header-text {
+ float: left;
+ font-size: 16px;
+ font-weight: bold;
+}
+
+#queue-listing-stats-container {
+ margin-left: 15px;
+ margin-top: 30px;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+}
+
+#queue-listing-stats {
+ font-size: 9px;
+ font-weight: bold;
+ color: #9f6000;
+ clear: left;
+ line-height: normal;
+ margin-left: 5px;
+}
+
+#queue-listing-loading-container {
+ float: left;
+ width: 16px;
+ height: 16px;
+ background-color: transparent;
+ margin-top: 4px;
+ margin-left: 3px;
+}
+
+/* queue listing table */
+
+#queue-listing-table {
+ position: absolute;
+ top: 100px;
+ left: 20px;
+ bottom: 20px;
+ right: 20px;
+ border: 1px solid #666;
+ overflow: hidden;
+}
+
+/* flowfile details */
+
+#flowfile-details-dialog {
+ display: none;
+ width: 800px;
+ height: 450px;
+ z-index: 1301;
+}
+
+#flowfile-details-dialog-content {
+ margin-top: -10px;
+ padding: 5px 11px
+}
+
+#flowfile-details-tabs {
+ background-color: transparent;
+ border-bottom: 3px solid #666666;
+ height: 21px;
+ width: 778px;
+}
+
+#flowfile-details-dialog div.details-tab {
+ background: url("../images/bgTabContainer.png") repeat-x scroll 0 0 #EEEEEE;
+ display: none;
+ height: 330px;
+ padding: 10px;
+}
+
+#flowfile-details-tab-content div.settings-left {
+ float: left;
+ width: 420px;
+}
+
+#flowfile-details-tab-content div.spacer {
+ float: left;
+ margin-right: 40px;
+}
+
+#flowfile-details-tab-content div.settings-right {
+ float: left;
+ width: 292px;
+}
+
+div.flowfile-header {
+ color: #264C58;
+ font-size: 11px;
+ font-weight: bold;
+ margin-bottom: 5px;
+}
+
+#flowfile-attributes-container {
+ height: 290px;
+ overflow: auto;
+ border: 1px solid #aaa;
+ padding: 5px;
+}
+
+div.detail-name {
+ float: left;
+ width: 145px;
+ color: #527991;
+ font-size: 10px;
+ font-weight: bold;
+ overflow: hidden;
+ white-space: nowrap;
+ line-height: normal;
+}
+
+div.detail-value {
+ float: left;
+ width: 270px;
+ overflow: hidden;
+ white-space: nowrap;
+ line-height: normal;
+}
+
+div.attribute-detail {
+ margin-bottom: 5px;
+}
+
+div.attribute-name {
+ float: left;
+ width: 225px;
+ color: #527991;
+ font-size: 10px;
+ font-weight: bold;
+ overflow: hidden;
+ white-space: nowrap;
+ line-height: normal;
+}
+
+div.attribute-value {
+ float: left;
+ width: 490px;
+ margin-left: 10px;
+ overflow: hidden;
+ white-space: nowrap;
+ line-height: normal;
+}
+
+#flowfile-attributes-header {
+ float: left;
+}
+
+div.flowfile-detail {
+ margin-bottom: 4px;
+}
+
+#content-download {
+ float: left;
+ margin: 0;
+}
+
+#content-view {
+ float: left;
+ margin-left: 5px;
+}
+
+div.content-detail-name {
+ color: #527991;
+ float: left;
+ font-size: 10px;
+ font-weight: bold;
+ line-height: normal;
+ overflow: hidden;
+ white-space: nowrap;
+ width: 90px;
+}
+
+div.content-detail-value {
+ width: 200px;
+ float: left;
+ line-height: normal;
+ overflow: hidden;
+ white-space: nowrap;
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/b330fd16/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/iconListQueue.png
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/iconListQueue.png b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/iconListQueue.png
new file mode 100644
index 0000000..9a60eef
Binary files /dev/null and b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/iconListQueue.png differ
http://git-wip-us.apache.org/repos/asf/nifi/blob/b330fd16/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
index 08f0e42..2f1bbd3 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
@@ -928,7 +928,7 @@ nf.Actions = (function () {
// cancel it
clearTimeout(dropRequestTimer);
- // cancel the provenance
+ // cancel the drop request
completeDropRequest();
}
}
@@ -1028,8 +1028,8 @@ nf.Actions = (function () {
// issue the request to delete the flow files
$.ajax({
- type: 'DELETE',
- url: connection.component.uri + '/contents',
+ type: 'POST',
+ url: connection.component.uri + '/drop-requests',
dataType: 'json'
}).done(function(response) {
// initialize the progress bar value
@@ -1045,7 +1045,24 @@ nf.Actions = (function () {
}
});
},
-
+
+ /**
+ * Lists the flow files in the specified connection.
+ *
+ * @param {selection} selection
+ */
+ listQueue: function (selection) {
+ if (selection.size() !== 1 || !nf.CanvasUtils.isConnection(selection)) {
+ return;
+ }
+
+ // get the connection data
+ var connection = selection.datum();
+
+ // list the flow files in the specified connection
+ nf.QueueListing.listQueue(connection);
+ },
+
/**
* Opens the fill color dialog for the component in the specified selection.
*
http://git-wip-us.apache.org/repos/asf/nifi/blob/b330fd16/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
index 428ddf2..005f7e3 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
@@ -1122,6 +1122,11 @@ nf.Canvas = (function () {
var configDetails = configResponse.config;
var loginDetails = loginResponse.config;
+ // store the content viewer url if available
+ if (!nf.Common.isBlank(configDetails.contentViewerUrl)) {
+ $('#nifi-content-viewer-url').text(configDetails.contentViewerUrl);
+ }
+
// when both request complete, load the application
isClusteredRequest.done(function () {
// get the auto refresh interval
@@ -1145,6 +1150,7 @@ nf.Canvas = (function () {
nf.Search.init();
nf.Settings.init();
nf.Actions.init();
+ nf.QueueListing.init();
// initialize the component behaviors
nf.Draggable.init();