From 9ea377b4088bf68b6cdf59f4029d6f2c1deb4070 Mon Sep 17 00:00:00 2001 From: Bryan Bende Date: Fri, 13 Oct 2017 16:32:01 -0400 Subject: [PATCH] NIFIREG-35 Initial commit of nifi-registry-client --- nifi-registry-client/pom.xml | 44 +++ .../nifi/registry/client/BucketClient.java | 85 ++++++ .../nifi/registry/client/FlowClient.java | 122 ++++++++ .../registry/client/FlowSnapshotClient.java | 81 +++++ .../nifi/registry/client/ItemsClient.java | 86 ++++++ .../registry/client/NiFiRegistryClient.java | 59 ++++ .../client/NiFiRegistryClientConfig.java | 253 ++++++++++++++++ .../client/NiFiRegistryException.java | 32 ++ .../client/impl/AbstractJerseyClient.java | 78 +++++ .../client/impl/JerseyBucketClient.java | 145 +++++++++ .../client/impl/JerseyFlowClient.java | 194 ++++++++++++ .../client/impl/JerseyFlowSnapshotClient.java | 132 ++++++++ .../client/impl/JerseyItemsClient.java | 103 +++++++ .../client/impl/JerseyNiFiRegistryClient.java | 159 ++++++++++ .../impl/TestJerseyNiFiRegistryClient.java | 281 ++++++++++++++++++ .../apache/nifi/registry/field/Fields.java | 10 +- .../nifi/registry}/params/SortOrder.java | 2 +- .../nifi/registry}/params/SortParameter.java | 2 +- .../registry/db/DatabaseMetadataService.java | 6 +- .../registry/service/MetadataService.java | 1 - .../service/{params => }/QueryParameters.java | 5 +- .../registry/service/RegistryService.java | 1 - .../db/TestDatabaseMetadataService.java | 2 +- .../nifi/registry/jetty/JettyServer.java | 5 + nifi-registry-security-utils/pom.xml | 43 +++ .../security/util/CertificateUtils.java | 2 +- .../security/util/KeyStoreUtils.java | 2 +- .../registry}/security/util/KeystoreType.java | 2 +- nifi-registry-web-api/pom.xml | 15 +- .../web/NiFiRegistryResourceConfig.java | 2 + .../registry/web/api/BucketFlowResource.java | 4 +- .../nifi/registry/web/api/BucketResource.java | 12 +- .../nifi/registry/web/api/FlowResource.java | 69 +++++ .../nifi/registry/web/api/ItemResource.java | 12 +- .../token/LoginAuthenticationToken.java | 2 +- pom.xml | 6 +- stop.sh | 23 ++ 37 files changed, 2040 insertions(+), 42 deletions(-) create mode 100644 nifi-registry-client/pom.xml create mode 100644 nifi-registry-client/src/main/java/org/apache/nifi/registry/client/BucketClient.java create mode 100644 nifi-registry-client/src/main/java/org/apache/nifi/registry/client/FlowClient.java create mode 100644 nifi-registry-client/src/main/java/org/apache/nifi/registry/client/FlowSnapshotClient.java create mode 100644 nifi-registry-client/src/main/java/org/apache/nifi/registry/client/ItemsClient.java create mode 100644 nifi-registry-client/src/main/java/org/apache/nifi/registry/client/NiFiRegistryClient.java create mode 100644 nifi-registry-client/src/main/java/org/apache/nifi/registry/client/NiFiRegistryClientConfig.java create mode 100644 nifi-registry-client/src/main/java/org/apache/nifi/registry/client/NiFiRegistryException.java create mode 100644 nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/AbstractJerseyClient.java create mode 100644 nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyBucketClient.java create mode 100644 nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyFlowClient.java create mode 100644 nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyFlowSnapshotClient.java create mode 100644 nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyItemsClient.java create mode 100644 nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyNiFiRegistryClient.java create mode 100644 nifi-registry-client/src/test/java/org/apache/nifi/registry/client/impl/TestJerseyNiFiRegistryClient.java rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/response/FieldsEntity.java => nifi-registry-data-model/src/main/java/org/apache/nifi/registry/field/Fields.java (88%) rename {nifi-registry-framework/src/main/java/org/apache/nifi/registry/service => nifi-registry-data-model/src/main/java/org/apache/nifi/registry}/params/SortOrder.java (96%) rename {nifi-registry-framework/src/main/java/org/apache/nifi/registry/service => nifi-registry-data-model/src/main/java/org/apache/nifi/registry}/params/SortParameter.java (98%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/{params => }/QueryParameters.java (95%) create mode 100644 nifi-registry-security-utils/pom.xml rename {nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web => nifi-registry-security-utils/src/main/java/org/apache/nifi/registry}/security/util/CertificateUtils.java (99%) rename {nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web => nifi-registry-security-utils/src/main/java/org/apache/nifi/registry}/security/util/KeyStoreUtils.java (98%) rename {nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web => nifi-registry-security-utils/src/main/java/org/apache/nifi/registry}/security/util/KeystoreType.java (94%) create mode 100644 nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java create mode 100755 stop.sh diff --git a/nifi-registry-client/pom.xml b/nifi-registry-client/pom.xml new file mode 100644 index 000000000..daf8dddba --- /dev/null +++ b/nifi-registry-client/pom.xml @@ -0,0 +1,44 @@ + + + + 4.0.0 + + org.apache.nifi.registry + nifi-registry + 0.0.1-SNAPSHOT + + + nifi-registry-client + jar + + + + org.apache.nifi.registry + nifi-registry-data-model + 0.0.1-SNAPSHOT + + + org.apache.nifi.registry + nifi-registry-security-utils + 0.0.1-SNAPSHOT + + + org.glassfish.jersey.core + jersey-client + + + org.glassfish.jersey.media + jersey-media-json-jackson + + + diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/BucketClient.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/BucketClient.java new file mode 100644 index 000000000..56763c48f --- /dev/null +++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/BucketClient.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.client; + +import org.apache.nifi.registry.bucket.Bucket; +import org.apache.nifi.registry.field.Fields; +import org.apache.nifi.registry.params.SortParameter; + +import java.io.IOException; +import java.util.List; + +/** + * Client for interacting with buckets. + */ +public interface BucketClient { + + /** + * Creates the given bucket. + * + * @param bucket the bucket to create + * @return the created bucket with containing identifier that was generated + */ + Bucket create(Bucket bucket) throws NiFiRegistryException, IOException; + + /** + * Gets the bucket with the given id. + * + * @param bucketId the id of the bucket to retrieve + * @return the bucket with the given id + */ + Bucket get(String bucketId) throws NiFiRegistryException, IOException; + + /** + * Updates the given bucket. Only the name and description can be updated. + * + * @param bucket the bucket with updates, must contain the id + * @return the updated bucket + */ + Bucket update(Bucket bucket) throws NiFiRegistryException, IOException; + + /** + * Deletes the bucket with the given id. + * + * @param bucketId the id of the bucket to delete + * @return the deleted bucket + */ + Bucket delete(String bucketId) throws NiFiRegistryException, IOException; + + /** + * Gets the fields that can be used to sort/search buckets. + * + * @return the bucket fields + */ + Fields getFields() throws NiFiRegistryException, IOException; + + /** + * Gets all buckets. + * + * @return the list of all buckets + */ + List getAll() throws NiFiRegistryException, IOException; + + /** + * Gets all buckets sorted by the given sort parameters. + * + * @param sorts the list of sort parameters, can be empty, but must be non-null + * @return the list of sorted buckets + */ + List getAll(List sorts) throws NiFiRegistryException, IOException; + +} diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/FlowClient.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/FlowClient.java new file mode 100644 index 000000000..206c4f1a3 --- /dev/null +++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/FlowClient.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.client; + +import org.apache.nifi.registry.field.Fields; +import org.apache.nifi.registry.flow.VersionedFlow; +import org.apache.nifi.registry.params.SortParameter; + +import java.io.IOException; +import java.util.List; + +/** + * Client for interacting with flows. + */ +public interface FlowClient { + + /** + * Create the given flow in the given bucket. + * + * @param bucketId a bucket id + * @param flow the flow to create + * @return the created flow with the identifier populated + * @throws NiFiRegistryException if an error is encountered other than IOException + * @throws IOException if an I/O error is encountered + */ + VersionedFlow create(String bucketId, VersionedFlow flow) throws NiFiRegistryException, IOException; + + /** + * Gets the flow with the given id in the given bucket. + * + * The list of snapshot metadata will NOT be populated. + * + * @param bucketId a bucket id + * @param flowId a flow id + * @return the flow with the given id in the given bucket + * @throws NiFiRegistryException if an error is encountered other than IOException + * @throws IOException if an I/O error is encountered + */ + VersionedFlow get(String bucketId, String flowId) throws NiFiRegistryException, IOException; + + /** + * Gets the flow with the given id in the given bucket. + * + * The list of snapshot metadata will be populated. + * + * @param bucketId a bucket id + * @param flowId a flow id + * @return the flow with the given id in the given bucket + * @throws NiFiRegistryException if an error is encountered other than IOException + * @throws IOException if an I/O error is encountered + */ + VersionedFlow getWithSnapshots(String bucketId, String flowId) throws NiFiRegistryException, IOException; + + /** + * Updates the given flow with in the given bucket. + * + * The identifier of the flow must be populated in the flow object, and only the name and description can be updated. + * + * @param bucketId a bucket id + * @param flow the flow with updates + * @return the updated flow + * @throws NiFiRegistryException if an error is encountered other than IOException + * @throws IOException if an I/O error is encountered + */ + VersionedFlow update(String bucketId, VersionedFlow flow) throws NiFiRegistryException, IOException; + + /** + * Deletes the flow with the given id in the given bucket. + * + * @param bucketId a bucket id + * @param flowId the id of the flow to delete + * @return the deleted flow + * @throws NiFiRegistryException if an error is encountered other than IOException + * @throws IOException if an I/O error is encountered + */ + VersionedFlow delete(String bucketId, String flowId) throws NiFiRegistryException, IOException; + + /** + * Gets the field info for flows. + * + * @return field info for flows + * @throws NiFiRegistryException if an error is encountered other than IOException + * @throws IOException if an I/O error is encountered + */ + Fields getFields() throws NiFiRegistryException, IOException; + + /** + * Gets the flows for a given bucket. + * + * @param bucketId a bucket id + * @return the flows in the given bucket + * @throws NiFiRegistryException if an error is encountered other than IOException + * @throws IOException if an I/O error is encountered + */ + List getByBucket(String bucketId) throws NiFiRegistryException, IOException; + + /** + * Gets the flows for a given bucket in the specified sorted order. + * + * @param bucketId a bucket id + * @param sorts sort parameters, can be empty, but must be non-null + * @return the flows in the given bucket + * @throws NiFiRegistryException if an error is encountered other than IOException + * @throws IOException if an I/O error is encountered + */ + List getByBucket(String bucketId, List sorts) throws NiFiRegistryException, IOException; + +} diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/FlowSnapshotClient.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/FlowSnapshotClient.java new file mode 100644 index 000000000..c9cea41e9 --- /dev/null +++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/FlowSnapshotClient.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.client; + +import org.apache.nifi.registry.flow.VersionedFlowSnapshot; +import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata; + +import java.io.IOException; +import java.util.List; + +/** + * Client for interacting with snapshots. + */ +public interface FlowSnapshotClient { + + /** + * Creates a new snapshot/version for the given flow. + * + * The snapshot object must have the version populated, and will receive an error if the submitted version is + * not the next one-up version. + * + * @param bucketId the bucket id + * @param flowId the flow id + * @param snapshot the new snapshot + * @return the created snapshot + * @throws NiFiRegistryException if an error is encountered other than IOException + * @throws IOException if an I/O error is encountered + */ + VersionedFlowSnapshot create(String bucketId, String flowId, VersionedFlowSnapshot snapshot) throws NiFiRegistryException, IOException; + + /** + * Gets the snapshot for the given bucket, flow, and version. + * + * @param bucketId the bucket id + * @param flowId the flow id + * @param version the version + * @return the snapshot with the given version of the given flow in the given bucket + * @throws NiFiRegistryException if an error is encountered other than IOException + * @throws IOException if an I/O error is encountered + */ + VersionedFlowSnapshot get(String bucketId, String flowId, int version) throws NiFiRegistryException, IOException; + + /** + * Gets the latest snapshot for the given flow. + * + * @param bucketId the bucket id + * @param flowId the flow id + * @return the snapshot with the latest version for the given flow + * @throws NiFiRegistryException if an error is encountered other than IOException + * @throws IOException if an I/O error is encountered + */ + VersionedFlowSnapshot getLatest(String bucketId, String flowId) throws NiFiRegistryException, IOException; + + /** + * Gets a list of the metadata for all snapshots of a given flow. + * + * The contents of each snapshot are not part of the response. + * + * @param bucketId the bucket id + * @param flowId the flow id + * @return the list of snapshot metadata + * @throws NiFiRegistryException if an error is encountered other than IOException + * @throws IOException if an I/O error is encountered + */ + List getSnapshotMetadata(String bucketId, String flowId) throws NiFiRegistryException, IOException; + +} diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/ItemsClient.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/ItemsClient.java new file mode 100644 index 000000000..f862f03fa --- /dev/null +++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/ItemsClient.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.client; + +import org.apache.nifi.registry.bucket.BucketItem; +import org.apache.nifi.registry.field.Fields; +import org.apache.nifi.registry.params.SortParameter; + +import java.io.IOException; +import java.util.List; + +/** + * Client for interacting with bucket items. + * + * Bucket items contain the common fields across anything stored in the registry. + * + * Each item contains a type field and a link to the URI of the specific item. + * + * i.e. The link field of a flow item would contain the URI to the specific flow. + */ +public interface ItemsClient { + + /** + * Gets all bucket items in the registry. + * + * @return the list of all bucket items + * @throws NiFiRegistryException if an error is encountered other than IOException + * @throws IOException if an I/O error is encountered + */ + List getAll() throws NiFiRegistryException, IOException; + + /** + * Gets all bucket items in the registry in sorted order. + * + * @param sortParameters the sort parameters + * @return the sorted list of items + * @throws NiFiRegistryException if an error is encountered other than IOException + * @throws IOException if an I/O error is encountered + */ + List getAll(List sortParameters) throws NiFiRegistryException, IOException; + + /** + * Gets all bucket items for the given bucket. + * + * @param bucketId the bucket id + * @return the list of items in the given bucket + * @throws NiFiRegistryException if an error is encountered other than IOException + * @throws IOException if an I/O error is encountered + */ + List getByBucket(String bucketId) throws NiFiRegistryException, IOException; + + /** + * Gets all bucket items in the given bucket in sorted order. + * + * @param bucketId the bucket id + * @param sortParameters the sort parameters + * @return the list of items + * @throws NiFiRegistryException if an error is encountered other than IOException + * @throws IOException if an I/O error is encountered + */ + List getByBucket(String bucketId, List sortParameters) throws NiFiRegistryException, IOException; + + /** + * Gets the field info for bucket items. + * + * @return the list of field info + * @throws NiFiRegistryException if an error is encountered other than IOException + * @throws IOException if an I/O error is encountered + */ + Fields getFields() throws NiFiRegistryException, IOException; + +} diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/NiFiRegistryClient.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/NiFiRegistryClient.java new file mode 100644 index 000000000..f53acf427 --- /dev/null +++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/NiFiRegistryClient.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.client; + +import java.io.Closeable; + +/** + * A client for interacting with the REST API of a NiFi registry instance. + */ +public interface NiFiRegistryClient extends Closeable { + + /** + * @return the client for interacting with buckets + */ + BucketClient getBucketClient(); + + /** + * @return the client for interacting with flows + */ + FlowClient getFlowClient(); + + /** + * @return the client for interacting with flows/snapshots + */ + FlowSnapshotClient getFlowSnapshotClient(); + + /** + * @return the client for interacting with bucket items + */ + ItemsClient getItemsClient(); + + /** + * The builder interface that implementations should provide for obtaining the client. + */ + interface Builder { + + Builder config(NiFiRegistryClientConfig clientConfig); + + NiFiRegistryClientConfig getConfig(); + + NiFiRegistryClient build(); + + } + +} diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/NiFiRegistryClientConfig.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/NiFiRegistryClientConfig.java new file mode 100644 index 000000000..6d5ddb11e --- /dev/null +++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/NiFiRegistryClientConfig.java @@ -0,0 +1,253 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.client; + +import org.apache.nifi.registry.security.util.KeyStoreUtils; +import org.apache.nifi.registry.security.util.KeystoreType; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.SecureRandom; + +/** + * Configuration for a NiFiRegistryClient. + */ +public class NiFiRegistryClientConfig { + + private final String baseUrl; + private final SSLContext sslContext; + private final String keystoreFilename; + private final String keystorePass; + private final String keyPass; + private final KeystoreType keystoreType; + private final String truststoreFilename; + private final String truststorePass; + private final KeystoreType truststoreType; + private final HostnameVerifier hostnameVerifier; + private final Integer readTimeout; + private final Integer connectTimeout; + + + private NiFiRegistryClientConfig(final Builder builder) { + this.baseUrl = builder.baseUrl; + this.sslContext = builder.sslContext; + this.keystoreFilename = builder.keystoreFilename; + this.keystorePass = builder.keystorePass; + this.keyPass = builder.keyPass; + this.keystoreType = builder.keystoreType; + this.truststoreFilename = builder.truststoreFilename; + this.truststorePass = builder.truststorePass; + this.truststoreType = builder.truststoreType; + this.hostnameVerifier = builder.hostnameVerifier; + this.readTimeout = builder.readTimeout; + this.connectTimeout = builder.connectTimeout; + } + + public String getBaseUrl() { + return baseUrl; + } + + public SSLContext getSslContext() { + if (sslContext != null) { + return sslContext; + } + + final KeyManagerFactory keyManagerFactory; + if (keystoreFilename != null && keystorePass != null && keystoreType != null) { + try { + // prepare the keystore + final KeyStore keyStore = KeyStoreUtils.getKeyStore(keystoreType.name()); + try (final InputStream keyStoreStream = new FileInputStream(new File(keystoreFilename))) { + keyStore.load(keyStoreStream, keystorePass.toCharArray()); + } + keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + + if (keyPass == null) { + keyManagerFactory.init(keyStore, keystorePass.toCharArray()); + } else { + keyManagerFactory.init(keyStore, keyPass.toCharArray()); + } + } catch (final Exception e) { + throw new IllegalStateException("Failed to load Keystore", e); + } + } else { + keyManagerFactory = null; + } + + final TrustManagerFactory trustManagerFactory; + if (truststoreFilename != null && truststorePass != null && truststoreType != null) { + try { + // prepare the truststore + final KeyStore trustStore = KeyStoreUtils.getTrustStore(truststoreType.name()); + try (final InputStream trustStoreStream = new FileInputStream(new File(truststoreFilename))) { + trustStore.load(trustStoreStream, truststorePass.toCharArray()); + } + trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(trustStore); + } catch (final Exception e) { + throw new IllegalStateException("Failed to load Truststore", e); + } + } else { + trustManagerFactory = null; + } + + if (keyManagerFactory != null && trustManagerFactory != null) { + try { + // initialize the ssl context + final SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom()); + sslContext.getDefaultSSLParameters().setNeedClientAuth(true); + + return sslContext; + } catch (final Exception e) { + throw new IllegalStateException("Created keystore and truststore but failed to initialize SSLContext", e); + } + } else { + return null; + } + } + + public String getKeystoreFilename() { + return keystoreFilename; + } + + public String getKeystorePass() { + return keystorePass; + } + + public String getKeyPass() { + return keyPass; + } + + public KeystoreType getKeystoreType() { + return keystoreType; + } + + public String getTruststoreFilename() { + return truststoreFilename; + } + + public String getTruststorePass() { + return truststorePass; + } + + public KeystoreType getTruststoreType() { + return truststoreType; + } + + public HostnameVerifier getHostnameVerifier() { + return hostnameVerifier; + } + + public Integer getReadTimeout() { + return readTimeout; + } + + public Integer getConnectTimeout() { + return connectTimeout; + } + + /** + * Builder for client configuration. + */ + public static class Builder { + + private String baseUrl; + private SSLContext sslContext; + private String keystoreFilename; + private String keystorePass; + private String keyPass; + private KeystoreType keystoreType; + private String truststoreFilename; + private String truststorePass; + private KeystoreType truststoreType; + private HostnameVerifier hostnameVerifier; + private Integer readTimeout; + private Integer connectTimeout; + + public Builder baseUrl(final String baseUrl) { + this.baseUrl = baseUrl; + return this; + } + + public Builder sslContext(final SSLContext sslContext) { + this.sslContext = sslContext; + return this; + } + + public Builder keystoreFilename(final String keystoreFilename) { + this.keystoreFilename = keystoreFilename; + return this; + } + + public Builder keystorePassword(final String keystorePass) { + this.keystorePass = keystorePass; + return this; + } + + public Builder keyPassword(final String keyPass) { + this.keyPass = keyPass; + return this; + } + + public Builder keystoreType(final KeystoreType keystoreType) { + this.keystoreType = keystoreType; + return this; + } + + public Builder truststoreFilename(final String truststoreFilename) { + this.truststoreFilename = truststoreFilename; + return this; + } + + public Builder truststorePassword(final String truststorePass) { + this.truststorePass = truststorePass; + return this; + } + + public Builder truststoreType(final KeystoreType truststoreType) { + this.truststoreType = truststoreType; + return this; + } + + public Builder hostnameVerifier(final HostnameVerifier hostnameVerifier) { + this.hostnameVerifier = hostnameVerifier; + return this; + } + + public Builder readTimeout(final Integer readTimeout) { + this.readTimeout = readTimeout; + return this; + } + + public Builder connectTimeout(final Integer connectTimeout) { + this.connectTimeout = connectTimeout; + return this; + } + + public NiFiRegistryClientConfig build() { + return new NiFiRegistryClientConfig(this); + } + + } +} diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/NiFiRegistryException.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/NiFiRegistryException.java new file mode 100644 index 000000000..273a03267 --- /dev/null +++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/NiFiRegistryException.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.client; + +/** + * Indicates an error interacting with the NiFi registry for a reason other than IOException. + */ +public class NiFiRegistryException extends Exception { + + public NiFiRegistryException(final String message) { + super(message); + } + + public NiFiRegistryException(final String message, final Throwable cause) { + super(message, cause); + } + +} diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/AbstractJerseyClient.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/AbstractJerseyClient.java new file mode 100644 index 000000000..ca28abf1f --- /dev/null +++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/AbstractJerseyClient.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.client.impl; + +import org.apache.nifi.registry.client.NiFiRegistryException; + +import java.io.IOException; + +/** + * Base class for the client operations to share exception handling. + */ +public class AbstractJerseyClient { + + /** + * Executes the given action and returns the result. + * + * @param action the action to execute + * @param errorMessage the message to use if a NiFiRegistryException is thrown + * @param the return type of the action + * @return the result of the action + * @throws NiFiRegistryException if any exception other than IOException is encountered + * @throws IOException if an I/O error occurs communicating with the registry + */ + protected T executeAction(final String errorMessage, final NiFiRegistryAction action) throws NiFiRegistryException, IOException { + try { + return action.execute(); + } catch (final Exception e) { + final Throwable ioeCause = getIOExceptionCause(e); + if (ioeCause == null) { + throw new NiFiRegistryException(errorMessage, e); + } else { + throw (IOException) ioeCause; + } + } + } + + /** + * An action to execute with the given return type. + * + * @param the return type of the action + */ + protected interface NiFiRegistryAction { + + T execute(); + + } + + /** + * @param e an exception that was encountered interacting with the registry + * @return the IOException that caused this exception, or null if the an IOException did not cause this exception + */ + protected Throwable getIOExceptionCause(final Throwable e) { + if (e == null) { + return null; + } + + if (e instanceof IOException) { + return e; + } + + return getIOExceptionCause(e.getCause()); + } + +} diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyBucketClient.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyBucketClient.java new file mode 100644 index 000000000..66bfbf276 --- /dev/null +++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyBucketClient.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.client.impl; + +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.registry.bucket.Bucket; +import org.apache.nifi.registry.client.BucketClient; +import org.apache.nifi.registry.client.NiFiRegistryException; +import org.apache.nifi.registry.field.Fields; +import org.apache.nifi.registry.params.SortParameter; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import java.io.IOException; +import java.util.List; + +/** + * Jersey implementation of BucketClient. + */ +public class JerseyBucketClient extends AbstractJerseyClient implements BucketClient { + + private final WebTarget bucketsTarget; + + public JerseyBucketClient(final WebTarget baseTarget) { + this.bucketsTarget = baseTarget.path("/buckets"); + } + + @Override + public Bucket create(final Bucket bucket) throws NiFiRegistryException, IOException { + if (bucket == null) { + throw new IllegalArgumentException("Bucket cannot be null"); + } + + return executeAction("Error creating bucket", () -> { + return bucketsTarget.request() + .post( + Entity.entity(bucket, MediaType.APPLICATION_JSON), + Bucket.class + ); + }); + + } + + @Override + public Bucket get(final String bucketId) throws NiFiRegistryException, IOException { + if (StringUtils.isBlank(bucketId)) { + throw new IllegalArgumentException("Bucket ID cannot be blank"); + } + + return executeAction("Error retrieving bucket", () -> { + return bucketsTarget + .path("/{bucketId}") + .resolveTemplate("bucketId", bucketId) + .request() + .get(Bucket.class); + }); + + } + + @Override + public Bucket update(final Bucket bucket) throws NiFiRegistryException, IOException { + if (bucket == null) { + throw new IllegalArgumentException("Bucket cannot be null"); + } + + if (StringUtils.isBlank(bucket.getIdentifier())) { + throw new IllegalArgumentException("Bucket Identifier must be provided"); + } + + return executeAction("Error updating bucket", () -> { + return bucketsTarget + .path("/{bucketId}") + .resolveTemplate("bucketId", bucket.getIdentifier()) + .request() + .put( + Entity.entity(bucket, MediaType.APPLICATION_JSON), + Bucket.class + ); + + }); + } + + @Override + public Bucket delete(final String bucketId) throws NiFiRegistryException, IOException { + if (StringUtils.isBlank(bucketId)) { + throw new IllegalArgumentException("Bucket ID cannot be blank"); + } + + return executeAction("Error deleting bucket", () -> { + return bucketsTarget + .path("/{bucketId}") + .resolveTemplate("bucketId", bucketId) + .request() + .delete(Bucket.class); + }); + } + + @Override + public Fields getFields() throws NiFiRegistryException, IOException { + return executeAction("Error retrieving bucket field info", () -> { + return bucketsTarget + .path("/fields") + .request() + .get(Fields.class); + }); + } + + @Override + public List getAll() throws NiFiRegistryException, IOException { + return executeAction("Error retrieving all buckets", () -> { + return bucketsTarget.request().get(List.class); + }); + } + + @Override + public List getAll(final List sorts) throws NiFiRegistryException, IOException { + if (sorts == null || sorts.size() == 0) { + return getAll(); + } + + return executeAction("Error retrieving all buckets", () -> { + WebTarget target = bucketsTarget; + for (final SortParameter sortParam : sorts) { + target = target.queryParam("sort", sortParam.toString()); + } + + return target.request().get(List.class); + }); + } +} diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyFlowClient.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyFlowClient.java new file mode 100644 index 000000000..977a98f79 --- /dev/null +++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyFlowClient.java @@ -0,0 +1,194 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.client.impl; + +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.registry.client.FlowClient; +import org.apache.nifi.registry.client.NiFiRegistryException; +import org.apache.nifi.registry.field.Fields; +import org.apache.nifi.registry.flow.VersionedFlow; +import org.apache.nifi.registry.params.SortParameter; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +/** + * Jersey implementation of FlowClient. + */ +public class JerseyFlowClient extends AbstractJerseyClient implements FlowClient { + + private final WebTarget flowsTarget; + private final WebTarget bucketFlowsTarget; + + public JerseyFlowClient(final WebTarget baseTarget) { + this.flowsTarget = baseTarget.path("/flows"); + this.bucketFlowsTarget = baseTarget.path("/buckets/{bucketId}/flows"); + } + + @Override + public VersionedFlow create(final String bucketId, final VersionedFlow flow) throws NiFiRegistryException, IOException { + if (StringUtils.isBlank(bucketId)) { + throw new IllegalArgumentException("Bucket Identifier cannot be blank"); + } + + if (flow == null) { + throw new IllegalArgumentException("VersionedFlow cannot be null"); + } + + return executeAction("Error creating flow", () -> { + return bucketFlowsTarget + .resolveTemplate("bucketId", bucketId) + .request() + .post( + Entity.entity(flow, MediaType.APPLICATION_JSON), + VersionedFlow.class + ); + }); + } + + @Override + public VersionedFlow get(final String bucketId, final String flowId) throws NiFiRegistryException, IOException { + if (StringUtils.isBlank(bucketId)) { + throw new IllegalArgumentException("Bucket Identifier cannot be blank"); + } + + if (StringUtils.isBlank(flowId)) { + throw new IllegalArgumentException("Flow Identifier cannot be blank"); + } + + return executeAction("Error retrieving flow", () -> { + return bucketFlowsTarget + .path("/{flowId}") + .resolveTemplate("bucketId", bucketId) + .resolveTemplate("flowId", flowId) + .request() + .get(VersionedFlow.class); + }); + } + + @Override + public VersionedFlow getWithSnapshots(final String bucketId, final String flowId) throws NiFiRegistryException, IOException { + if (StringUtils.isBlank(bucketId)) { + throw new IllegalArgumentException("Bucket Identifier cannot be blank"); + } + + if (StringUtils.isBlank(flowId)) { + throw new IllegalArgumentException("Flow Identifier cannot be blank"); + } + + return executeAction("Error retrieving flow", () -> { + return bucketFlowsTarget + .path("/{flowId}") + .resolveTemplate("bucketId", bucketId) + .resolveTemplate("flowId", flowId) + .queryParam("verbose", "true") + .request() + .get(VersionedFlow.class); + }); + } + + @Override + public VersionedFlow update(final String bucketId, final VersionedFlow flow) throws NiFiRegistryException, IOException { + if (StringUtils.isBlank(bucketId)) { + throw new IllegalArgumentException("Bucket Identifier cannot be blank"); + } + + if (flow == null) { + throw new IllegalArgumentException("VersionedFlow cannot be null"); + } + + if (StringUtils.isBlank(flow.getIdentifier())) { + throw new IllegalArgumentException("VersionedFlow identifier must be provided"); + } + + return executeAction("Error updating flow", () -> { + return bucketFlowsTarget + .path("/{flowId}") + .resolveTemplate("bucketId", bucketId) + .resolveTemplate("flowId", flow.getIdentifier()) + .request() + .put( + Entity.entity(flow, MediaType.APPLICATION_JSON), + VersionedFlow.class + ); + }); + } + + @Override + public VersionedFlow delete(final String bucketId, final String flowId) throws NiFiRegistryException, IOException { + if (StringUtils.isBlank(bucketId)) { + throw new IllegalArgumentException("Bucket Identifier cannot be blank"); + } + + if (StringUtils.isBlank(flowId)) { + throw new IllegalArgumentException("Flow Identifier cannot be blank"); + } + + return executeAction("Error deleting flow", () -> { + return bucketFlowsTarget + .path("/{flowId}") + .resolveTemplate("bucketId", bucketId) + .resolveTemplate("flowId", flowId) + .request() + .delete(VersionedFlow.class); + }); + } + + @Override + public Fields getFields() throws NiFiRegistryException, IOException { + return executeAction("Error retrieving fields info for flows", () -> { + return flowsTarget + .path("/fields") + .request() + .get(Fields.class); + }); + } + + @Override + public List getByBucket(final String bucketId) throws NiFiRegistryException, IOException { + return getByBucket(bucketId, Collections.emptyList()); + } + + @Override + public List getByBucket(final String bucketId, final List sorts) throws NiFiRegistryException, IOException { + if (StringUtils.isBlank(bucketId)) { + throw new IllegalArgumentException("Bucket Identifier cannot be blank"); + } + + if (sorts == null) { + throw new IllegalArgumentException("Sorts cannot be null"); + } + + return executeAction("Error getting flows for bucket", () -> { + WebTarget target = bucketFlowsTarget; + for (final SortParameter sortParam : sorts) { + target = target.queryParam("sort", sortParam.toString()); + } + + return target + .resolveTemplate("bucketId", bucketId) + .request() + .get(List.class); + }); + } + + +} diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyFlowSnapshotClient.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyFlowSnapshotClient.java new file mode 100644 index 000000000..ce3fabd31 --- /dev/null +++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyFlowSnapshotClient.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.client.impl; + +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.registry.client.FlowSnapshotClient; +import org.apache.nifi.registry.client.NiFiRegistryException; +import org.apache.nifi.registry.flow.VersionedFlowSnapshot; +import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import java.io.IOException; +import java.util.List; + +/** + * Jersey implementation of FlowSnapshotClient. + */ +public class JerseyFlowSnapshotClient extends AbstractJerseyClient implements FlowSnapshotClient { + + final WebTarget flowSnapshotTarget; + + public JerseyFlowSnapshotClient(final WebTarget baseTarget) { + this.flowSnapshotTarget = baseTarget.path("/buckets/{bucketId}/flows/{flowId}/versions"); + } + + @Override + public VersionedFlowSnapshot create(final String bucketId, final String flowId, final VersionedFlowSnapshot snapshot) + throws NiFiRegistryException, IOException { + if (StringUtils.isBlank(bucketId)) { + throw new IllegalArgumentException("Bucket Identifier cannot be blank"); + } + + if (StringUtils.isBlank(flowId)) { + throw new IllegalArgumentException("Flow Identifier cannot be blank"); + } + + return executeAction("Error creating snapshot", () -> { + return flowSnapshotTarget + .resolveTemplate("bucketId", bucketId) + .resolveTemplate("flowId", flowId) + .request() + .post( + Entity.entity(snapshot, MediaType.APPLICATION_JSON), + VersionedFlowSnapshot.class + ); + }); + } + + @Override + public VersionedFlowSnapshot get(final String bucketId, final String flowId, final int version) + throws NiFiRegistryException, IOException { + if (StringUtils.isBlank(bucketId)) { + throw new IllegalArgumentException("Bucket Identifier cannot be blank"); + } + + if (StringUtils.isBlank(flowId)) { + throw new IllegalArgumentException("Flow Identifier cannot be blank"); + } + + if (version < 1) { + throw new IllegalArgumentException("Version must be greater than 1"); + } + + return executeAction("Error retrieving flow snapshot", () -> { + return flowSnapshotTarget + .path("/{version}") + .resolveTemplate("bucketId", bucketId) + .resolveTemplate("flowId", flowId) + .resolveTemplate("version", version) + .request() + .get(VersionedFlowSnapshot.class); + }); + } + + @Override + public VersionedFlowSnapshot getLatest(final String bucketId, final String flowId) + throws NiFiRegistryException, IOException { + if (StringUtils.isBlank(bucketId)) { + throw new IllegalArgumentException("Bucket Identifier cannot be blank"); + } + + if (StringUtils.isBlank(flowId)) { + throw new IllegalArgumentException("Flow Identifier cannot be blank"); + } + + return executeAction("Error retrieving latest snapshot", () -> { + return flowSnapshotTarget + .path("/latest") + .resolveTemplate("bucketId", bucketId) + .resolveTemplate("flowId", flowId) + .request() + .get(VersionedFlowSnapshot.class); + }); + } + + @Override + public List getSnapshotMetadata(final String bucketId, final String flowId) + throws NiFiRegistryException, IOException { + if (StringUtils.isBlank(bucketId)) { + throw new IllegalArgumentException("Bucket Identifier cannot be blank"); + } + + if (StringUtils.isBlank(flowId)) { + throw new IllegalArgumentException("Flow Identifier cannot be blank"); + } + + return executeAction("Error retrieving snapshot metadata", () -> { + return flowSnapshotTarget + .resolveTemplate("bucketId", bucketId) + .resolveTemplate("flowId", flowId) + .request() + .get(List.class); + }); + } + +} diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyItemsClient.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyItemsClient.java new file mode 100644 index 000000000..271566814 --- /dev/null +++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyItemsClient.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.client.impl; + +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.registry.bucket.BucketItem; +import org.apache.nifi.registry.client.ItemsClient; +import org.apache.nifi.registry.client.NiFiRegistryException; +import org.apache.nifi.registry.field.Fields; +import org.apache.nifi.registry.params.SortParameter; + +import javax.ws.rs.client.WebTarget; +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +/** + * Jersey implementation of ItemsClient. + */ +public class JerseyItemsClient extends AbstractJerseyClient implements ItemsClient { + + private final WebTarget itemsTarget; + + public JerseyItemsClient(final WebTarget baseTarget) { + this.itemsTarget = baseTarget.path("/items"); + } + + @Override + public List getAll() throws NiFiRegistryException, IOException { + return getAll(Collections.emptyList()); + } + + @Override + public List getAll(final List sorts) throws NiFiRegistryException, IOException { + if (sorts == null) { + throw new IllegalArgumentException("Sort Parameters cannot be null"); + } + + return executeAction("", () -> { + WebTarget target = itemsTarget; + for (final SortParameter sortParam : sorts) { + target = target.queryParam("sort", sortParam.toString()); + } + + return target.request().get(List.class); + }); + } + + @Override + public List getByBucket(final String bucketId) throws NiFiRegistryException, IOException { + return getByBucket(bucketId, Collections.emptyList()); + } + + @Override + public List getByBucket(final String bucketId, final List sorts) + throws NiFiRegistryException, IOException { + if (StringUtils.isBlank(bucketId)) { + throw new IllegalArgumentException("Bucket Identifier cannot be blank"); + } + + if (sorts == null) { + throw new IllegalArgumentException("Sort Parameters cannot be null"); + } + + return executeAction("", () -> { + WebTarget target = itemsTarget + .path("/{bucketId}") + .resolveTemplate("bucketId", bucketId); + + for (final SortParameter sortParam : sorts) { + target = target.queryParam("sort", sortParam.toString()); + } + + return target.request().get(List.class); + }); + } + + @Override + public Fields getFields() throws NiFiRegistryException, IOException { + return executeAction("", () -> { + return itemsTarget + .path("/fields") + .request() + .get(Fields.class); + + }); + } + +} diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyNiFiRegistryClient.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyNiFiRegistryClient.java new file mode 100644 index 000000000..bf616b8eb --- /dev/null +++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyNiFiRegistryClient.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.client.impl; + +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.registry.client.BucketClient; +import org.apache.nifi.registry.client.FlowClient; +import org.apache.nifi.registry.client.FlowSnapshotClient; +import org.apache.nifi.registry.client.ItemsClient; +import org.apache.nifi.registry.client.NiFiRegistryClient; +import org.apache.nifi.registry.client.NiFiRegistryClientConfig; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.ClientProperties; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.WebTarget; +import java.io.IOException; +import java.net.URI; + +/** + * A NiFiRegistryClient that uses Jersey Client. + */ +public class JerseyNiFiRegistryClient implements NiFiRegistryClient { + + static final String NIFI_REGISTRY_CONTEXT = "nifi-registry-api"; + static final int DEFAULT_CONNECT_TIMEOUT = 10000; + static final int DEFAULT_READ_TIMEOUT = 10000; + + private final Client client; + private final BucketClient bucketClient; + private final FlowClient flowClient; + private final FlowSnapshotClient flowSnapshotClient; + private final ItemsClient itemsClient; + + private JerseyNiFiRegistryClient(final NiFiRegistryClient.Builder builder) { + final NiFiRegistryClientConfig registryClientConfig = builder.getConfig(); + if (registryClientConfig == null) { + throw new IllegalArgumentException("NiFiRegistryClientConfig cannot be null"); + } + + String baseUrl = registryClientConfig.getBaseUrl(); + if (StringUtils.isBlank(baseUrl)) { + throw new IllegalArgumentException("Base URL cannot be blank"); + } + + if (baseUrl.endsWith("/")) { + baseUrl = baseUrl.substring(0, baseUrl.length() - 2); + } + + if (!baseUrl.endsWith(NIFI_REGISTRY_CONTEXT)) { + baseUrl = baseUrl + "/" + NIFI_REGISTRY_CONTEXT; + } + + try { + new URI(baseUrl); + } catch (final Exception e) { + throw new IllegalArgumentException("Invalid base URL: " + e.getMessage(), e); + } + + final SSLContext sslContext = registryClientConfig.getSslContext(); + final HostnameVerifier hostnameVerifier = registryClientConfig.getHostnameVerifier(); + + final ClientBuilder clientBuilder = ClientBuilder.newBuilder(); + if (sslContext != null) { + clientBuilder.sslContext(sslContext); + } + if (hostnameVerifier != null) { + clientBuilder.hostnameVerifier(hostnameVerifier); + } + + final int connectTimeout = registryClientConfig.getConnectTimeout() == null ? DEFAULT_CONNECT_TIMEOUT : registryClientConfig.getConnectTimeout(); + final int readTimeout = registryClientConfig.getReadTimeout() == null ? DEFAULT_READ_TIMEOUT : registryClientConfig.getReadTimeout(); + + final ClientConfig clientConfig = new ClientConfig(); + clientConfig.property(ClientProperties.CONNECT_TIMEOUT, connectTimeout); + clientConfig.property(ClientProperties.READ_TIMEOUT, readTimeout); + clientBuilder.withConfig(clientConfig); + this.client = clientBuilder.build(); + + final WebTarget baseTarget = client.target(baseUrl); + this.bucketClient = new JerseyBucketClient(baseTarget); + this.flowClient = new JerseyFlowClient(baseTarget); + this.flowSnapshotClient = new JerseyFlowSnapshotClient(baseTarget); + this.itemsClient = new JerseyItemsClient(baseTarget); + } + + @Override + public BucketClient getBucketClient() { + return this.bucketClient; + } + + @Override + public FlowClient getFlowClient() { + return this.flowClient; + } + + @Override + public FlowSnapshotClient getFlowSnapshotClient() { + return this.flowSnapshotClient; + } + + @Override + public ItemsClient getItemsClient() { + return this.itemsClient; + } + + @Override + public void close() throws IOException { + if (this.client != null) { + try { + this.client.close(); + } catch (Exception e) { + + } + } + } + + /** + * Builder for creating a JerseyNiFiRegistryClient. + */ + public static class Builder implements NiFiRegistryClient.Builder { + + private NiFiRegistryClientConfig clientConfig; + + @Override + public Builder config(final NiFiRegistryClientConfig clientConfig) { + this.clientConfig = clientConfig; + return this; + } + + @Override + public NiFiRegistryClientConfig getConfig() { + return clientConfig; + } + + @Override + public NiFiRegistryClient build() { + return new JerseyNiFiRegistryClient(this); + } + + } +} diff --git a/nifi-registry-client/src/test/java/org/apache/nifi/registry/client/impl/TestJerseyNiFiRegistryClient.java b/nifi-registry-client/src/test/java/org/apache/nifi/registry/client/impl/TestJerseyNiFiRegistryClient.java new file mode 100644 index 000000000..3f80a7add --- /dev/null +++ b/nifi-registry-client/src/test/java/org/apache/nifi/registry/client/impl/TestJerseyNiFiRegistryClient.java @@ -0,0 +1,281 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.client.impl; + +import org.apache.nifi.registry.bucket.Bucket; +import org.apache.nifi.registry.bucket.BucketItem; +import org.apache.nifi.registry.client.BucketClient; +import org.apache.nifi.registry.client.FlowClient; +import org.apache.nifi.registry.client.FlowSnapshotClient; +import org.apache.nifi.registry.client.ItemsClient; +import org.apache.nifi.registry.client.NiFiRegistryClient; +import org.apache.nifi.registry.client.NiFiRegistryClientConfig; +import org.apache.nifi.registry.client.NiFiRegistryException; +import org.apache.nifi.registry.field.Fields; +import org.apache.nifi.registry.flow.VersionedFlow; +import org.apache.nifi.registry.flow.VersionedFlowSnapshot; +import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata; +import org.apache.nifi.registry.flow.VersionedProcessGroup; +import org.apache.nifi.registry.params.SortOrder; +import org.apache.nifi.registry.params.SortParameter; +import org.junit.Assert; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TestJerseyNiFiRegistryClient { + + public static final Logger LOGGER = LoggerFactory.getLogger(TestJerseyNiFiRegistryClient.class); + + public static void main(String[] args) { + final NiFiRegistryClientConfig config = new NiFiRegistryClientConfig.Builder() + .baseUrl("http://localhost:8080") + .build(); + + final NiFiRegistryClient client = new JerseyNiFiRegistryClient.Builder() + .config(config) + .build(); + + final BucketClient bucketClient = client.getBucketClient(); + + try { + // ---------------------- TEST BUCKETS --------------------------// + + // create buckets + final int numBuckets = 10; + final List createdBuckets = new ArrayList<>(); + + for (int i=0; i < numBuckets; i++) { + final Bucket createdBucket = createBucket(bucketClient, i); + System.out.println("Created bucket # " + i + " with id " + createdBucket.getIdentifier()); + createdBuckets.add(createdBucket); + } + + // get each bucket + for (final Bucket bucket : createdBuckets) { + final Bucket retrievedBucket = bucketClient.get(bucket.getIdentifier()); + Assert.assertNotNull(retrievedBucket); + System.out.println("Retrieved bucket " + retrievedBucket.getIdentifier()); + } + + //final Bucket nonExistentBucket = bucketClient.get("does-not-exist"); + //Assert.assertNull(nonExistentBucket); + + // get bucket fields + final Fields bucketFields = bucketClient.getFields(); + Assert.assertNotNull(bucketFields); + System.out.println("Retrieved bucket fields, size = " + bucketFields.getFields().size()); + Assert.assertTrue(bucketFields.getFields().size() > 0); + + // get all buckets + final List allBuckets = bucketClient.getAll(); + System.out.println("Retrieved buckets, size = " + allBuckets.size()); + Assert.assertEquals(numBuckets, allBuckets.size()); + + // get all buckets with sorting + final SortParameter sortParam = new SortParameter("created", SortOrder.ASC); + final List allBucketsSorted = bucketClient.getAll(Arrays.asList(sortParam)); + System.out.println("Retrieved sorted buckets, size = " + allBucketsSorted.size()); + Assert.assertEquals(numBuckets, allBucketsSorted.size()); + + // update each bucket + for (final Bucket bucket : createdBuckets) { + final Bucket bucketUpdate = new Bucket(); + bucketUpdate.setIdentifier(bucket.getIdentifier()); + bucketUpdate.setDescription(bucket.getDescription() + " UPDATE"); + + final Bucket updatedBucket = bucketClient.update(bucketUpdate); + Assert.assertNotNull(updatedBucket); + System.out.println("Updated bucket " + updatedBucket.getIdentifier()); + } + + // ---------------------- TEST FLOWS --------------------------// + + final FlowClient flowClient = client.getFlowClient(); + + // create flows + final Bucket flowsBucket = createdBuckets.get(0); + + final VersionedFlow flow1 = createFlow(flowClient, flowsBucket, 1); + System.out.println("Created flow # 1 with id " + flow1.getIdentifier()); + + final VersionedFlow flow2 = createFlow(flowClient, flowsBucket, 2); + System.out.println("Created flow # 2 with id " + flow2.getIdentifier()); + + // get flow + final VersionedFlow retrievedFlow1 = flowClient.get(flowsBucket.getIdentifier(), flow1.getIdentifier()); + Assert.assertNotNull(retrievedFlow1); + System.out.println("Retrieved flow # 1 with id " + retrievedFlow1.getIdentifier()); + + final VersionedFlow retrievedFlow2 = flowClient.get(flowsBucket.getIdentifier(), flow2.getIdentifier()); + Assert.assertNotNull(retrievedFlow2); + System.out.println("Retrieved flow # 2 with id " + retrievedFlow2.getIdentifier()); + + // update flows + final VersionedFlow flow1Update = new VersionedFlow(); + flow1Update.setIdentifier(flow1.getIdentifier()); + flow1Update.setName(flow1.getName() + " UPDATED"); + + final VersionedFlow updatedFlow1 = flowClient.update(flowsBucket.getIdentifier(), flow1Update); + Assert.assertNotNull(updatedFlow1); + System.out.println("Updated flow # 1 with id " + updatedFlow1.getIdentifier()); + + // get flow fields + final Fields flowFields = flowClient.getFields(); + Assert.assertNotNull(flowFields); + System.out.println("Retrieved flow fields, size = " + flowFields.getFields().size()); + Assert.assertTrue(flowFields.getFields().size() > 0); + + // get flows in bucket + final List flowsInBucket = flowClient.getByBucket(flowsBucket.getIdentifier()); + Assert.assertNotNull(flowsInBucket); + Assert.assertEquals(2, flowsInBucket.size()); + + // get flows in bucket with sorting + final SortParameter flowsSortParam = new SortParameter("created", SortOrder.ASC); + final List flowsInBucketSorted = flowClient.getByBucket(flowsBucket.getIdentifier(), Arrays.asList(flowsSortParam)); + Assert.assertNotNull(flowsInBucketSorted); + Assert.assertEquals(2, flowsInBucketSorted.size()); + + // ---------------------- TEST SNAPSHOTS --------------------------// + + final FlowSnapshotClient snapshotClient = client.getFlowSnapshotClient(); + + // create snapshots + final VersionedFlow snapshotFlow = flow1; + + final VersionedFlowSnapshot snapshot1 = createSnapshot(snapshotClient, snapshotFlow, 1); + System.out.println("Created snapshot # 1 with version " + snapshot1.getSnapshotMetadata().getVersion()); + + final VersionedFlowSnapshot snapshot2 = createSnapshot(snapshotClient, snapshotFlow, 2); + System.out.println("Created snapshot # 2 with version " + snapshot2.getSnapshotMetadata().getVersion()); + + // get snapshot + final VersionedFlowSnapshot retrievedSnapshot1 = snapshotClient.get(snapshotFlow.getBucketIdentifier(), snapshotFlow.getIdentifier(), 1); + Assert.assertNotNull(retrievedSnapshot1); + System.out.println("Retrieved snapshot # 1 with version " + retrievedSnapshot1.getSnapshotMetadata().getVersion()); + + final VersionedFlowSnapshot retrievedSnapshot2 = snapshotClient.get(snapshotFlow.getBucketIdentifier(), snapshotFlow.getIdentifier(), 2); + Assert.assertNotNull(retrievedSnapshot2); + System.out.println("Retrieved snapshot # 2 with version " + retrievedSnapshot2.getSnapshotMetadata().getVersion()); + + // get latest + final VersionedFlowSnapshot retrievedSnapshotLatest = snapshotClient.getLatest(snapshotFlow.getBucketIdentifier(), snapshotFlow.getIdentifier()); + Assert.assertNotNull(retrievedSnapshotLatest); + Assert.assertEquals(snapshot2.getSnapshotMetadata().getVersion(), retrievedSnapshotLatest.getSnapshotMetadata().getVersion()); + System.out.println("Retrieved latest snapshot with version " + retrievedSnapshotLatest.getSnapshotMetadata().getVersion()); + + // get metadata + final List retrievedMetadata = snapshotClient.getSnapshotMetadata(snapshotFlow.getBucketIdentifier(), snapshotFlow.getIdentifier()); + Assert.assertNotNull(retrievedMetadata); + Assert.assertEquals(2, retrievedMetadata.size()); + + // ---------------------- TEST ITEMS --------------------------// + + final ItemsClient itemsClient = client.getItemsClient(); + + // get fields + final Fields itemFields = itemsClient.getFields(); + Assert.assertNotNull(itemFields.getFields()); + Assert.assertTrue(itemFields.getFields().size() > 0); + + // get all items + final List allItems = itemsClient.getAll(); + Assert.assertEquals(2, allItems.size()); + + // get all items with sorting + final SortParameter itemsSortParam = new SortParameter("created", SortOrder.ASC); + final List allItemsSorted = itemsClient.getAll(Arrays.asList(itemsSortParam)); + Assert.assertEquals(2, allItemsSorted.size()); + + // get items for bucket + final List bucketItems = itemsClient.getByBucket(flowsBucket.getIdentifier()); + Assert.assertEquals(2, bucketItems.size()); + + // get items for bucket with sorting + final List bucketItemsSorted = itemsClient.getByBucket(flowsBucket.getIdentifier(), Arrays.asList(itemsSortParam)); + Assert.assertEquals(2, bucketItemsSorted.size()); + + // ---------------------- DELETE DATA --------------------------// + + final VersionedFlow deletedFlow1 = flowClient.delete(flowsBucket.getIdentifier(), flow1.getIdentifier()); + Assert.assertNotNull(deletedFlow1); + System.out.println("Deleted flow " + deletedFlow1.getIdentifier()); + + final VersionedFlow deletedFlow2 = flowClient.delete(flowsBucket.getIdentifier(), flow2.getIdentifier()); + Assert.assertNotNull(deletedFlow2); + System.out.println("Deleted flow " + deletedFlow2.getIdentifier()); + + // delete each bucket + for (final Bucket bucket : createdBuckets) { + final Bucket deletedBucket = bucketClient.delete(bucket.getIdentifier()); + Assert.assertNotNull(deletedBucket); + System.out.println("Deleted bucket " + deletedBucket.getIdentifier()); + } + Assert.assertEquals(0, bucketClient.getAll().size()); + + System.out.println("!!! SUCCESS !!!"); + + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + } finally { + try { + client.close(); + } catch (Exception e) { + + } + } + } + + private static Bucket createBucket(BucketClient bucketClient, int num) throws IOException, NiFiRegistryException { + final Bucket bucket = new Bucket(); + bucket.setName("Bucket #" + num); + bucket.setDescription("This is bucket #" + num); + return bucketClient.create(bucket); + } + + private static VersionedFlow createFlow(FlowClient client, Bucket bucket, int num) throws IOException, NiFiRegistryException { + final VersionedFlow versionedFlow = new VersionedFlow(); + versionedFlow.setName(bucket.getName() + " Flow #" + num); + versionedFlow.setDescription("This is " + bucket.getName() + " flow #" + num); + return client.create(bucket.getIdentifier(), versionedFlow); + } + + private static VersionedFlowSnapshot createSnapshot(FlowSnapshotClient client, VersionedFlow flow, int num) throws IOException, NiFiRegistryException { + final VersionedFlowSnapshotMetadata snapshotMetadata = new VersionedFlowSnapshotMetadata(); + snapshotMetadata.setBucketIdentifier(flow.getBucketIdentifier()); + snapshotMetadata.setFlowIdentifier(flow.getIdentifier()); + snapshotMetadata.setFlowName(flow.getName()); + snapshotMetadata.setVersion(num); + snapshotMetadata.setComments("This is snapshot #" + num); + + final VersionedProcessGroup snapshotContents = new VersionedProcessGroup(); + snapshotContents.setIdentifier("pg1"); + snapshotContents.setName("Process Group 1"); + + final VersionedFlowSnapshot snapshot = new VersionedFlowSnapshot(); + snapshot.setSnapshotMetadata(snapshotMetadata); + snapshot.setFlowContents(snapshotContents); + + return client.create(flow.getBucketIdentifier(), flow.getIdentifier(), snapshot); + } + +} diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/response/FieldsEntity.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/field/Fields.java similarity index 88% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/response/FieldsEntity.java rename to nifi-registry-data-model/src/main/java/org/apache/nifi/registry/field/Fields.java index 3dec2b0cc..d1aac6d80 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/response/FieldsEntity.java +++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/field/Fields.java @@ -14,15 +14,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.response; +package org.apache.nifi.registry.field; import java.util.Set; -public class FieldsEntity { +public class Fields { private Set fields; - public FieldsEntity(Set fields) { + public Fields() { + + } + + public Fields(Set fields) { this.fields = fields; } diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/params/SortOrder.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/params/SortOrder.java similarity index 96% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/params/SortOrder.java rename to nifi-registry-data-model/src/main/java/org/apache/nifi/registry/params/SortOrder.java index 43a3016e7..8e571de2b 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/params/SortOrder.java +++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/params/SortOrder.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.service.params; +package org.apache.nifi.registry.params; public enum SortOrder { diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/params/SortParameter.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/params/SortParameter.java similarity index 98% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/params/SortParameter.java rename to nifi-registry-data-model/src/main/java/org/apache/nifi/registry/params/SortParameter.java index 8443f4b51..d4a1add59 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/params/SortParameter.java +++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/params/SortParameter.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.service.params; +package org.apache.nifi.registry.params; /** * Sort parameter made up of a field and a sort order. diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/DatabaseMetadataService.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/DatabaseMetadataService.java index 07d530af1..0710ef37c 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/DatabaseMetadataService.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/DatabaseMetadataService.java @@ -28,9 +28,9 @@ import org.apache.nifi.registry.db.repository.FlowRepository; import org.apache.nifi.registry.db.repository.FlowSnapshotRepository; import org.apache.nifi.registry.service.MetadataService; -import org.apache.nifi.registry.service.params.QueryParameters; -import org.apache.nifi.registry.service.params.SortOrder; -import org.apache.nifi.registry.service.params.SortParameter; +import org.apache.nifi.registry.service.QueryParameters; +import org.apache.nifi.registry.params.SortOrder; +import org.apache.nifi.registry.params.SortParameter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/MetadataService.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/MetadataService.java index d754b63c4..1c6e91868 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/MetadataService.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/MetadataService.java @@ -20,7 +20,6 @@ import org.apache.nifi.registry.db.entity.BucketItemEntity; import org.apache.nifi.registry.db.entity.FlowEntity; import org.apache.nifi.registry.db.entity.FlowSnapshotEntity; -import org.apache.nifi.registry.service.params.QueryParameters; import java.util.List; import java.util.Set; diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/params/QueryParameters.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/QueryParameters.java similarity index 95% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/params/QueryParameters.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/QueryParameters.java index 9f30f7575..99ef9dd3b 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/params/QueryParameters.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/QueryParameters.java @@ -14,7 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.service.params; +package org.apache.nifi.registry.service; + +import org.apache.nifi.registry.params.SortOrder; +import org.apache.nifi.registry.params.SortParameter; import java.util.ArrayList; import java.util.Collection; diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/RegistryService.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/RegistryService.java index f0e00d498..e78dd77d0 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/RegistryService.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/RegistryService.java @@ -32,7 +32,6 @@ import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata; import org.apache.nifi.registry.provider.flow.StandardFlowSnapshotContext; import org.apache.nifi.registry.serialization.Serializer; -import org.apache.nifi.registry.service.params.QueryParameters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; diff --git a/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/TestDatabaseMetadataService.java b/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/TestDatabaseMetadataService.java index 86e07bf9b..4fab2806e 100644 --- a/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/TestDatabaseMetadataService.java +++ b/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/TestDatabaseMetadataService.java @@ -20,7 +20,7 @@ import org.apache.nifi.registry.db.entity.BucketItemEntity; import org.apache.nifi.registry.db.entity.BucketItemEntityType; import org.apache.nifi.registry.db.entity.FlowEntity; -import org.apache.nifi.registry.service.params.QueryParameters; +import org.apache.nifi.registry.service.QueryParameters; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; diff --git a/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/JettyServer.java b/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/JettyServer.java index 536e99093..6cdcfb888 100644 --- a/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/JettyServer.java +++ b/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/JettyServer.java @@ -124,6 +124,11 @@ private void configureConnectors() { throw new IllegalStateException("Invalid HTTPs port: " + port); } + if (StringUtils.isBlank(properties.getKeyStorePath())) { + throw new IllegalStateException(NiFiRegistryProperties.SECURITY_KEYSTORE + + " must be provided to configure Jetty for HTTPs"); + } + logger.info("Configuring Jetty for HTTPs on port: " + port); // add some secure config diff --git a/nifi-registry-security-utils/pom.xml b/nifi-registry-security-utils/pom.xml new file mode 100644 index 000000000..376484a22 --- /dev/null +++ b/nifi-registry-security-utils/pom.xml @@ -0,0 +1,43 @@ + + + + 4.0.0 + + org.apache.nifi.registry + nifi-registry + 0.0.1-SNAPSHOT + + + nifi-registry-security-utils + jar + + + + + + org.bouncycastle + bcprov-jdk15on + 1.55 + + + org.bouncycastle + bcpkix-jdk15on + 1.55 + + + org.apache.commons + commons-lang3 + + + + diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/util/CertificateUtils.java b/nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/CertificateUtils.java similarity index 99% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/util/CertificateUtils.java rename to nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/CertificateUtils.java index 3282c4286..bbc7d7cc6 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/util/CertificateUtils.java +++ b/nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/CertificateUtils.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security.util; +package org.apache.nifi.registry.security.util; import org.apache.commons.lang3.StringUtils; import org.bouncycastle.asn1.ASN1Encodable; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/util/KeyStoreUtils.java b/nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/KeyStoreUtils.java similarity index 98% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/util/KeyStoreUtils.java rename to nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/KeyStoreUtils.java index 877c5b72c..71f1ce0af 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/util/KeyStoreUtils.java +++ b/nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/KeyStoreUtils.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.nifi.registry.web.security.util; +package org.apache.nifi.registry.security.util; import org.apache.commons.lang3.StringUtils; import org.bouncycastle.jce.provider.BouncyCastleProvider; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/util/KeystoreType.java b/nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/KeystoreType.java similarity index 94% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/util/KeystoreType.java rename to nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/KeystoreType.java index f519b581a..f143e5a1a 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/util/KeystoreType.java +++ b/nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/KeystoreType.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security.util; +package org.apache.nifi.registry.security.util; /** * Keystore types. diff --git a/nifi-registry-web-api/pom.xml b/nifi-registry-web-api/pom.xml index 4aa7f15bb..3e1308a6d 100644 --- a/nifi-registry-web-api/pom.xml +++ b/nifi-registry-web-api/pom.xml @@ -137,6 +137,11 @@ 0.0.1-SNAPSHOT provided + + org.apache.nifi.registry + nifi-registry-security-utils + 0.0.1-SNAPSHOT + org.apache.commons commons-lang3 @@ -180,15 +185,5 @@ guava 18.0 - - org.bouncycastle - bcprov-jdk15on - 1.55 - - - org.bouncycastle - bcpkix-jdk15on - 1.55 - diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java index 5baea4b48..c07a1fa5e 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java @@ -20,6 +20,7 @@ import org.apache.nifi.registry.web.api.AccessResource; import org.apache.nifi.registry.web.api.BucketFlowResource; import org.apache.nifi.registry.web.api.BucketResource; +import org.apache.nifi.registry.web.api.FlowResource; import org.apache.nifi.registry.web.api.ItemResource; import org.apache.nifi.registry.web.api.ResourceResource; import org.apache.nifi.registry.web.api.TenantResource; @@ -76,6 +77,7 @@ public NiFiRegistryResourceConfig(@Context ServletContext servletContext) { register(AccessResource.class); register(BucketResource.class); register(BucketFlowResource.class); + register(FlowResource.class); register(ItemResource.class); register(ResourceResource.class); register(TenantResource.class); diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java index 520092a40..de602a643 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java @@ -31,8 +31,8 @@ import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata; import org.apache.nifi.registry.service.AuthorizationService; import org.apache.nifi.registry.service.RegistryService; -import org.apache.nifi.registry.service.params.QueryParameters; -import org.apache.nifi.registry.service.params.SortParameter; +import org.apache.nifi.registry.service.QueryParameters; +import org.apache.nifi.registry.params.SortParameter; import org.apache.nifi.registry.web.link.LinkService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java index 711b8ed02..4614ae412 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java @@ -30,10 +30,10 @@ import org.apache.nifi.registry.bucket.BucketItem; import org.apache.nifi.registry.service.AuthorizationService; import org.apache.nifi.registry.service.RegistryService; -import org.apache.nifi.registry.service.params.QueryParameters; -import org.apache.nifi.registry.service.params.SortParameter; +import org.apache.nifi.registry.service.QueryParameters; +import org.apache.nifi.registry.params.SortParameter; import org.apache.nifi.registry.web.link.LinkService; -import org.apache.nifi.registry.web.response.FieldsEntity; +import org.apache.nifi.registry.field.Fields; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -239,12 +239,12 @@ public Response deleteBucket( @Produces(MediaType.APPLICATION_JSON) @ApiOperation( value = "Retrieves field names for searching or sorting on buckets.", - response = FieldsEntity.class + response = Fields.class ) public Response getAvailableBucketFields() { final Set bucketFields = registryService.getBucketFields(); - final FieldsEntity fieldsEntity = new FieldsEntity(bucketFields); - return Response.status(Response.Status.OK).entity(fieldsEntity).build(); + final Fields fields = new Fields(bucketFields); + return Response.status(Response.Status.OK).entity(fields).build(); } private void authorizeAccess(RequestAction actionType) { diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java new file mode 100644 index 000000000..6e7f34789 --- /dev/null +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.web.api; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.nifi.registry.authorization.Authorizer; +import org.apache.nifi.registry.field.Fields; +import org.apache.nifi.registry.service.AuthorizationService; +import org.apache.nifi.registry.service.RegistryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.util.Set; + +@Component +@Path("/flows") +@Api( + value = "/flows", + description = "Gets metadata about flows." +) +public class FlowResource extends AuthorizableApplicationResource { + + private final RegistryService registryService; + + @Autowired + public FlowResource( + final RegistryService registryService, + final AuthorizationService authorizationService, + final Authorizer authorizer) { + super(authorizer, authorizationService); + this.registryService = registryService; + } + + @GET + @Path("fields") + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Retrieves the available field names that can be used for searching or sorting on flows.", + response = Fields.class + ) + public Response getAvailableFlowFields() { + final Set flowFields = registryService.getFlowFields(); + final Fields fields = new Fields(flowFields); + return Response.status(Response.Status.OK).entity(fields).build(); + } + +} diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java index 01df333de..458f0f070 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java @@ -24,10 +24,10 @@ import org.apache.nifi.registry.bucket.BucketItem; import org.apache.nifi.registry.service.AuthorizationService; import org.apache.nifi.registry.service.RegistryService; -import org.apache.nifi.registry.service.params.QueryParameters; -import org.apache.nifi.registry.service.params.SortParameter; +import org.apache.nifi.registry.service.QueryParameters; +import org.apache.nifi.registry.params.SortParameter; import org.apache.nifi.registry.web.link.LinkService; -import org.apache.nifi.registry.web.response.FieldsEntity; +import org.apache.nifi.registry.field.Fields; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -143,12 +143,12 @@ public Response getItems( @Produces(MediaType.APPLICATION_JSON) @ApiOperation( value = "Retrieves the available field names for searching or sorting on bucket items.", - response = FieldsEntity.class + response = Fields.class ) public Response getAvailableBucketItemFields() { final Set bucketFields = registryService.getBucketItemFields(); - final FieldsEntity fieldsEntity = new FieldsEntity(bucketFields); - return Response.status(Response.Status.OK).entity(fieldsEntity).build(); + final Fields fields = new Fields(bucketFields); + return Response.status(Response.Status.OK).entity(fields).build(); } } diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/token/LoginAuthenticationToken.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/token/LoginAuthenticationToken.java index cbb3b1f88..86c86346a 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/token/LoginAuthenticationToken.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/token/LoginAuthenticationToken.java @@ -16,7 +16,7 @@ */ package org.apache.nifi.registry.web.security.token; -import org.apache.nifi.registry.web.security.util.CertificateUtils; +import org.apache.nifi.registry.security.util.CertificateUtils; import org.springframework.security.authentication.AbstractAuthenticationToken; import java.text.SimpleDateFormat; diff --git a/pom.xml b/pom.xml index b472b4947..c8ab4def8 100644 --- a/pom.xml +++ b/pom.xml @@ -39,14 +39,16 @@ nifi-registry-runtime nifi-registry-security-api nifi-registry-security-api-impl - nifi-registry-framework + nifi-registry-security-utils + nifi-registry-framework nifi-registry-provider-api nifi-registry-web-api nifi-registry-web-ui nifi-registry-web-docs nifi-registry-bootstrap nifi-registry-docs - nifi-registry-assembly + nifi-registry-assembly + nifi-registry-client https://nifi.apache.org/registry.html diff --git a/stop.sh b/stop.sh new file mode 100755 index 000000000..852f51412 --- /dev/null +++ b/stop.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# +# 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. + +REGISTRY_SCRIPT=`find nifi-registry-assembly/target/ -name nifi-registry.sh | head -1` +REGISTRY_BIN_DIR=$(dirname "${REGISTRY_SCRIPT}") +REGISTRY_DIR=$REGISTRY_BIN_DIR/.. + +./${REGISTRY_SCRIPT} stop +