http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/NiFiClientConfig.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/NiFiClientConfig.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/NiFiClientConfig.java new file mode 100644 index 0000000..d5f4981 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/NiFiClientConfig.java @@ -0,0 +1,271 @@ +/* + * 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.toolkit.cli.impl.client.nifi; + +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.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +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 NiFiClient. + */ +public class NiFiClientConfig { + + public static final String DEFAULT_PROTOCOL = "TLSv1.2"; + + 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 String protocol; + private final HostnameVerifier hostnameVerifier; + private final Integer readTimeout; + private final Integer connectTimeout; + + + private NiFiClientConfig(final NiFiClientConfig.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.protocol = builder.protocol == null ? DEFAULT_PROTOCOL : builder.protocol; + 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 + KeyManager[] keyManagers = keyManagerFactory != null ? keyManagerFactory.getKeyManagers() : null; + TrustManager[] trustManagers = trustManagerFactory != null ? trustManagerFactory.getTrustManagers() : null; + final SSLContext sslContext = SSLContext.getInstance(getProtocol()); + sslContext.init(keyManagers, trustManagers, 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 String getProtocol() { + return protocol; + } + + 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 String protocol; + private HostnameVerifier hostnameVerifier; + private Integer readTimeout; + private Integer connectTimeout; + + public NiFiClientConfig.Builder baseUrl(final String baseUrl) { + this.baseUrl = baseUrl; + return this; + } + + public NiFiClientConfig.Builder sslContext(final SSLContext sslContext) { + this.sslContext = sslContext; + return this; + } + + public NiFiClientConfig.Builder keystoreFilename(final String keystoreFilename) { + this.keystoreFilename = keystoreFilename; + return this; + } + + public NiFiClientConfig.Builder keystorePassword(final String keystorePass) { + this.keystorePass = keystorePass; + return this; + } + + public NiFiClientConfig.Builder keyPassword(final String keyPass) { + this.keyPass = keyPass; + return this; + } + + public NiFiClientConfig.Builder keystoreType(final KeystoreType keystoreType) { + this.keystoreType = keystoreType; + return this; + } + + public NiFiClientConfig.Builder truststoreFilename(final String truststoreFilename) { + this.truststoreFilename = truststoreFilename; + return this; + } + + public NiFiClientConfig.Builder truststorePassword(final String truststorePass) { + this.truststorePass = truststorePass; + return this; + } + + public NiFiClientConfig.Builder truststoreType(final KeystoreType truststoreType) { + this.truststoreType = truststoreType; + return this; + } + + public NiFiClientConfig.Builder protocol(final String protocol) { + this.protocol = protocol; + return this; + } + + public NiFiClientConfig.Builder hostnameVerifier(final HostnameVerifier hostnameVerifier) { + this.hostnameVerifier = hostnameVerifier; + return this; + } + + public NiFiClientConfig.Builder readTimeout(final Integer readTimeout) { + this.readTimeout = readTimeout; + return this; + } + + public NiFiClientConfig.Builder connectTimeout(final Integer connectTimeout) { + this.connectTimeout = connectTimeout; + return this; + } + + public NiFiClientConfig build() { + return new NiFiClientConfig(this); + } + + } +}
http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/NiFiClientException.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/NiFiClientException.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/NiFiClientException.java new file mode 100644 index 0000000..8b392e8 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/NiFiClientException.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.toolkit.cli.impl.client.nifi; + +public class NiFiClientException extends Exception { + + public NiFiClientException(final String message) { + super(message); + } + + public NiFiClientException(final String message, final Throwable cause) { + super(message, cause); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ProcessGroupClient.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ProcessGroupClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ProcessGroupClient.java new file mode 100644 index 0000000..037c310 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ProcessGroupClient.java @@ -0,0 +1,38 @@ +/* + * 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.toolkit.cli.impl.client.nifi; + +import org.apache.nifi.web.api.entity.ProcessGroupEntity; +import org.apache.nifi.web.api.entity.VariableRegistryEntity; + +import java.io.IOException; + +/** + * Client for ProcessGroupResource. + */ +public interface ProcessGroupClient { + + ProcessGroupEntity createProcessGroup(String parentGroupdId, ProcessGroupEntity entity) + throws NiFiClientException, IOException; + + ProcessGroupEntity getProcessGroup(String processGroupId) throws NiFiClientException, IOException; + + ProcessGroupEntity updateProcessGroup(ProcessGroupEntity entity) throws NiFiClientException, IOException; + + VariableRegistryEntity getVariables(String processGroupId) throws NiFiClientException, IOException; + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/AbstractJerseyClient.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/AbstractJerseyClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/AbstractJerseyClient.java new file mode 100644 index 0000000..7767a64 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/AbstractJerseyClient.java @@ -0,0 +1,120 @@ +/* + * 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.toolkit.cli.impl.client.nifi.impl; + +import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.client.Invocation; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Base class for the client operations to share exception handling. + * + * Sub-classes should always execute a request from getRequestBuilder(target) to ensure proper headers are sent. + */ +public class AbstractJerseyClient { + + private final Map<String,String> headers; + + public AbstractJerseyClient(final Map<String, String> headers) { + this.headers = headers == null ? Collections.emptyMap() : Collections.unmodifiableMap(new HashMap<>(headers)); + } + + protected Map<String,String> getHeaders() { + return headers; + } + + /** + * Creates a new Invocation.Builder for the given WebTarget with the headers added to the builder. + * + * @param webTarget the target for the request + * @return the builder for the target with the headers added + */ + protected Invocation.Builder getRequestBuilder(final WebTarget webTarget) { + final Invocation.Builder requestBuilder = webTarget.request(); + headers.entrySet().stream().forEach(e -> requestBuilder.header(e.getKey(), e.getValue())); + return requestBuilder; + } + + /** + * 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 <T> the return type of the action + * @return the result of the action + * @throws NiFiClientException if any exception other than IOException is encountered + * @throws IOException if an I/O error occurs communicating with the registry + */ + protected <T> T executeAction(final String errorMessage, final NiFiAction<T> action) throws NiFiClientException, IOException { + try { + return action.execute(); + } catch (final Exception e) { + final Throwable ioeCause = getIOExceptionCause(e); + + if (ioeCause == null) { + final StringBuilder errorMessageBuilder = new StringBuilder(errorMessage); + + // see if we have a WebApplicationException, and if so add the response body to the error message + if (e instanceof WebApplicationException) { + final Response response = ((WebApplicationException) e).getResponse(); + final String responseBody = response.readEntity(String.class); + errorMessageBuilder.append(": ").append(responseBody); + } + + throw new NiFiClientException(errorMessageBuilder.toString(), e); + } else { + throw (IOException) ioeCause; + } + } + } + + + /** + * An action to execute with the given return type. + * + * @param <T> the return type of the action + */ + protected interface NiFiAction<T> { + + 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()); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyControllerClient.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyControllerClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyControllerClient.java new file mode 100644 index 0000000..9c9ffc4 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyControllerClient.java @@ -0,0 +1,107 @@ +/* + * 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.toolkit.cli.impl.client.nifi.impl; + +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.toolkit.cli.impl.client.nifi.ControllerClient; +import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException; +import org.apache.nifi.web.api.entity.RegistryClientEntity; +import org.apache.nifi.web.api.entity.RegistryClientsEntity; + +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.Map; + +/** + * Jersey implementation of ControllerClient. + */ +public class JerseyControllerClient extends AbstractJerseyClient implements ControllerClient { + + private final WebTarget controllerTarget; + + public JerseyControllerClient(final WebTarget baseTarget) { + this(baseTarget, Collections.emptyMap()); + } + + public JerseyControllerClient(final WebTarget baseTarget, final Map<String,String> headers) { + super(headers); + this.controllerTarget = baseTarget.path("/controller"); + } + + @Override + public RegistryClientsEntity getRegistryClients() throws NiFiClientException, IOException { + return executeAction("Error retrieving registry clients", () -> { + final WebTarget target = controllerTarget.path("registry-clients"); + return getRequestBuilder(target).get(RegistryClientsEntity.class); + }); + } + + @Override + public RegistryClientEntity getRegistryClient(final String id) throws NiFiClientException, IOException { + if (StringUtils.isBlank(id)) { + throw new IllegalArgumentException("Registry client id cannot be null"); + } + + final WebTarget target = controllerTarget + .path("registry-clients/{id}") + .resolveTemplate("id", id); + + return getRequestBuilder(target).get(RegistryClientEntity.class); + } + + @Override + public RegistryClientEntity createRegistryClient(final RegistryClientEntity registryClient) throws NiFiClientException, IOException { + if (registryClient == null) { + throw new IllegalArgumentException("Registry client entity cannot be null"); + } + + return executeAction("Error creating registry client", () -> { + final WebTarget target = controllerTarget.path("registry-clients"); + + return getRequestBuilder(target).post( + Entity.entity(registryClient, MediaType.APPLICATION_JSON), + RegistryClientEntity.class + ); + }); + } + + @Override + public RegistryClientEntity updateRegistryClient(final RegistryClientEntity registryClient) throws NiFiClientException, IOException { + if (registryClient == null) { + throw new IllegalArgumentException("Registry client entity cannot be null"); + } + + if (StringUtils.isBlank(registryClient.getId())) { + throw new IllegalArgumentException("Registry client entity must contain an id"); + } + + return executeAction("Error updating registry client", () -> { + final WebTarget target = controllerTarget + .path("registry-clients/{id}") + .resolveTemplate("id", registryClient.getId()); + + return getRequestBuilder(target).put( + Entity.entity(registryClient, MediaType.APPLICATION_JSON), + RegistryClientEntity.class + ); + }); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyFlowClient.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyFlowClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyFlowClient.java new file mode 100644 index 0000000..5af34be --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyFlowClient.java @@ -0,0 +1,105 @@ +/* + * 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.toolkit.cli.impl.client.nifi.impl; + +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.toolkit.cli.impl.client.nifi.FlowClient; +import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException; +import org.apache.nifi.web.api.entity.CurrentUserEntity; +import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity; +import org.apache.nifi.web.api.entity.ScheduleComponentsEntity; + +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.Map; + +/** + * Jersey implementation of FlowClient. + */ +public class JerseyFlowClient extends AbstractJerseyClient implements FlowClient { + + static final String ROOT = "root"; + + private final WebTarget flowTarget; + + public JerseyFlowClient(final WebTarget baseTarget) { + this(baseTarget, Collections.emptyMap()); + } + + public JerseyFlowClient(final WebTarget baseTarget, final Map<String,String> headers) { + super(headers); + this.flowTarget = baseTarget.path("/flow"); + } + + @Override + public CurrentUserEntity getCurrentUser() throws NiFiClientException, IOException { + return executeAction("Error retrieving current", () -> { + final WebTarget target = flowTarget.path("current-user"); + return getRequestBuilder(target).get(CurrentUserEntity.class); + }); + } + + @Override + public String getRootGroupId() throws NiFiClientException, IOException { + final ProcessGroupFlowEntity entity = getProcessGroup(ROOT); + return entity.getProcessGroupFlow().getId(); + } + + @Override + public ProcessGroupFlowEntity getProcessGroup(final String id) throws NiFiClientException, IOException { + if (StringUtils.isBlank(id)) { + throw new IllegalArgumentException("Process group id cannot be null"); + } + + return executeAction("Error retrieving process group flow", () -> { + final WebTarget target = flowTarget + .path("process-groups/{id}") + .resolveTemplate("id", id); + + return getRequestBuilder(target).get(ProcessGroupFlowEntity.class); + }); + } + + @Override + public ScheduleComponentsEntity scheduleProcessGroupComponents( + final String processGroupId, final ScheduleComponentsEntity scheduleComponentsEntity) + throws NiFiClientException, IOException { + + if (StringUtils.isBlank(processGroupId)) { + throw new IllegalArgumentException("Process group id cannot be null"); + } + + if (scheduleComponentsEntity == null) { + throw new IllegalArgumentException("ScheduleComponentsEntity cannot be null"); + } + + scheduleComponentsEntity.setId(processGroupId); + + return executeAction("Error scheduling components", () -> { + final WebTarget target = flowTarget + .path("process-groups/{id}") + .resolveTemplate("id", processGroupId); + + return getRequestBuilder(target).put( + Entity.entity(scheduleComponentsEntity, MediaType.APPLICATION_JSON_TYPE), + ScheduleComponentsEntity.class); + }); + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyNiFiClient.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyNiFiClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyNiFiClient.java new file mode 100644 index 0000000..091f5db --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyNiFiClient.java @@ -0,0 +1,240 @@ +/* + * 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.toolkit.cli.impl.client.nifi.impl; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector; +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.registry.security.util.ProxiedEntitiesUtils; +import org.apache.nifi.toolkit.cli.impl.client.nifi.ControllerClient; +import org.apache.nifi.toolkit.cli.impl.client.nifi.FlowClient; +import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient; +import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientConfig; +import org.apache.nifi.toolkit.cli.impl.client.nifi.ProcessGroupClient; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJaxbJsonProvider; + +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; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Jersey implementation of NiFiClient. + */ +public class JerseyNiFiClient implements NiFiClient { + + static final String NIFI_CONTEXT = "nifi-api"; + static final int DEFAULT_CONNECT_TIMEOUT = 10000; + static final int DEFAULT_READ_TIMEOUT = 10000; + + static final String AUTHORIZATION_HEADER = "Authorization"; + static final String BEARER = "Bearer"; + + private final Client client; + private final WebTarget baseTarget; + + private JerseyNiFiClient(final Builder builder) { + final NiFiClientConfig clientConfig = builder.getConfig(); + if (clientConfig == null) { + throw new IllegalArgumentException("NiFiClientConfig cannot be null"); + } + + String baseUrl = clientConfig.getBaseUrl(); + if (StringUtils.isBlank(baseUrl)) { + throw new IllegalArgumentException("Base URL cannot be blank"); + } + + if (baseUrl.endsWith("/")) { + baseUrl = baseUrl.substring(0, baseUrl.length() - 1); + } + + if (!baseUrl.endsWith(NIFI_CONTEXT)) { + baseUrl = baseUrl + "/" + NIFI_CONTEXT; + } + + try { + new URI(baseUrl); + } catch (final Exception e) { + throw new IllegalArgumentException("Invalid base URL: " + e.getMessage(), e); + } + + final SSLContext sslContext = clientConfig.getSslContext(); + final HostnameVerifier hostnameVerifier = clientConfig.getHostnameVerifier(); + + final ClientBuilder clientBuilder = ClientBuilder.newBuilder(); + if (sslContext != null) { + clientBuilder.sslContext(sslContext); + } + if (hostnameVerifier != null) { + clientBuilder.hostnameVerifier(hostnameVerifier); + } + + final int connectTimeout = clientConfig.getConnectTimeout() == null ? DEFAULT_CONNECT_TIMEOUT : clientConfig.getConnectTimeout(); + final int readTimeout = clientConfig.getReadTimeout() == null ? DEFAULT_READ_TIMEOUT : clientConfig.getReadTimeout(); + + final ClientConfig jerseyClientConfig = new ClientConfig(); + jerseyClientConfig.property(ClientProperties.CONNECT_TIMEOUT, connectTimeout); + jerseyClientConfig.property(ClientProperties.READ_TIMEOUT, readTimeout); + jerseyClientConfig.register(jacksonJaxbJsonProvider()); + clientBuilder.withConfig(jerseyClientConfig); + this.client = clientBuilder.build(); + + this.baseTarget = client.target(baseUrl); + } + + @Override + public ControllerClient getControllerClient() { + return new JerseyControllerClient(baseTarget); + } + + @Override + public ControllerClient getControllerClientForProxiedEntities(final String... proxiedEntity) { + final Map<String,String> headers = getHeaders(proxiedEntity); + return new JerseyControllerClient(baseTarget, headers); + } + + @Override + public ControllerClient getControllerClientForToken(final String base64token) { + final Map<String,String> headers = getHeadersWithToken(base64token); + return new JerseyControllerClient(baseTarget, headers); + } + + @Override + public FlowClient getFlowClient() { + return new JerseyFlowClient(baseTarget); + } + + @Override + public FlowClient getFlowClientForProxiedEntities(String... proxiedEntity) { + final Map<String,String> headers = getHeaders(proxiedEntity); + return new JerseyFlowClient(baseTarget, headers); + } + + @Override + public FlowClient getFlowClientForToken(String base64token) { + final Map<String,String> headers = getHeadersWithToken(base64token); + return new JerseyFlowClient(baseTarget, headers); + } + + @Override + public ProcessGroupClient getProcessGroupClient() { + return new JerseyProcessGroupClient(baseTarget); + } + + @Override + public ProcessGroupClient getProcessGroupClientForProxiedEntities(String... proxiedEntity) { + final Map<String,String> headers = getHeaders(proxiedEntity); + return new JerseyProcessGroupClient(baseTarget, headers); + } + + @Override + public ProcessGroupClient getProcessGroupClientForToken(String base64token) { + final Map<String,String> headers = getHeadersWithToken(base64token); + return new JerseyProcessGroupClient(baseTarget, headers); + } + + @Override + public void close() throws IOException { + if (this.client != null) { + try { + this.client.close(); + } catch (Exception e) { + + } + } + } + + private Map<String,String> getHeadersWithToken(final String base64token) { + if (StringUtils.isBlank(base64token)) { + throw new IllegalArgumentException("Token cannot be null"); + } + + final Map<String,String> headers = new HashMap<>(); + headers.put(AUTHORIZATION_HEADER, BEARER + " " + base64token); + return headers; + } + + private Map<String,String> getHeaders(final String[] proxiedEntities) { + final String proxiedEntitiesValue = getProxiedEntitesValue(proxiedEntities); + + final Map<String,String> headers = new HashMap<>(); + if (proxiedEntitiesValue != null) { + headers.put(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxiedEntitiesValue); + } + return headers; + } + + private String getProxiedEntitesValue(final String[] proxiedEntities) { + if (proxiedEntities == null) { + return null; + } + + final List<String> proxiedEntityChain = Arrays.stream(proxiedEntities) + .map(ProxiedEntitiesUtils::formatProxyDn).collect(Collectors.toList()); + return StringUtils.join(proxiedEntityChain, ""); + } + + /** + * Builder for creating a JerseyNiFiClient. + */ + public static class Builder implements NiFiClient.Builder { + + private NiFiClientConfig clientConfig; + + @Override + public JerseyNiFiClient.Builder config(final NiFiClientConfig clientConfig) { + this.clientConfig = clientConfig; + return this; + } + + @Override + public NiFiClientConfig getConfig() { + return clientConfig; + } + + @Override + public NiFiClient build() { + return new JerseyNiFiClient(this); + } + + } + + private static JacksonJaxbJsonProvider jacksonJaxbJsonProvider() { + JacksonJaxbJsonProvider jacksonJaxbJsonProvider = new JacksonJaxbJsonProvider(); + + ObjectMapper mapper = new ObjectMapper(); + mapper.setDefaultPropertyInclusion(JsonInclude.Value.construct(JsonInclude.Include.NON_NULL, JsonInclude.Include.NON_NULL)); + mapper.setAnnotationIntrospector(new JaxbAnnotationIntrospector(mapper.getTypeFactory())); + // Ignore unknown properties so that deployed client remain compatible with future versions of NiFi that add new fields + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + jacksonJaxbJsonProvider.setMapper(mapper); + return jacksonJaxbJsonProvider; + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyProcessGroupClient.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyProcessGroupClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyProcessGroupClient.java new file mode 100644 index 0000000..65bac4f --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyProcessGroupClient.java @@ -0,0 +1,121 @@ +/* + * 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.toolkit.cli.impl.client.nifi.impl; + +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException; +import org.apache.nifi.toolkit.cli.impl.client.nifi.ProcessGroupClient; +import org.apache.nifi.web.api.entity.ProcessGroupEntity; +import org.apache.nifi.web.api.entity.VariableRegistryEntity; + +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.Map; + +/** + * Jersey implementation of ProcessGroupClient. + */ +public class JerseyProcessGroupClient extends AbstractJerseyClient implements ProcessGroupClient { + + private final WebTarget processGroupsTarget; + + public JerseyProcessGroupClient(final WebTarget baseTarget) { + this(baseTarget, Collections.emptyMap()); + } + + public JerseyProcessGroupClient(final WebTarget baseTarget, final Map<String,String> headers) { + super(headers); + this.processGroupsTarget = baseTarget.path("/process-groups"); + } + + @Override + public ProcessGroupEntity createProcessGroup(final String parentGroupdId, final ProcessGroupEntity entity) + throws NiFiClientException, IOException { + + if (StringUtils.isBlank(parentGroupdId)) { + throw new IllegalArgumentException("Parent process group id cannot be null or blank"); + } + + if (entity == null){ + throw new IllegalArgumentException("Process group entity cannot be null"); + } + + return executeAction("Error creating process group", () -> { + final WebTarget target = processGroupsTarget + .path("{id}/process-groups") + .resolveTemplate("id", parentGroupdId); + + return getRequestBuilder(target).post( + Entity.entity(entity, MediaType.APPLICATION_JSON_TYPE), + ProcessGroupEntity.class + ); + }); + } + + @Override + public ProcessGroupEntity getProcessGroup(final String processGroupId) throws NiFiClientException, IOException { + if (StringUtils.isBlank(processGroupId)) { + throw new IllegalArgumentException("Process group id cannot be null or blank"); + } + + return executeAction("Error getting process group", () -> { + final WebTarget target = processGroupsTarget + .path("{id}") + .resolveTemplate("id", processGroupId); + + return getRequestBuilder(target).get(ProcessGroupEntity.class); + }); + } + + @Override + public ProcessGroupEntity updateProcessGroup(final ProcessGroupEntity entity) + throws NiFiClientException, IOException { + + if (entity == null){ + throw new IllegalArgumentException("Process group entity cannot be null"); + } + + return executeAction("Error updating process group", () -> { + final WebTarget target = processGroupsTarget + .path("{id}") + .resolveTemplate("id", entity.getId()); + + return getRequestBuilder(target).put( + Entity.entity(entity, MediaType.APPLICATION_JSON_TYPE), + ProcessGroupEntity.class + ); + }); + } + + @Override + public VariableRegistryEntity getVariables(final String processGroupId) throws NiFiClientException, IOException { + if (StringUtils.isBlank(processGroupId)) { + throw new IllegalArgumentException("Parent process group id cannot be null or blank"); + } + + return executeAction("Error getting variables for process group", () -> { + final WebTarget target = processGroupsTarget + .path("{id}/variable-registry") + .resolveTemplate("id", processGroupId); + + return getRequestBuilder(target).get(VariableRegistryEntity.class); + }); + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java new file mode 100644 index 0000000..0b82947 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java @@ -0,0 +1,220 @@ +/* + * 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.toolkit.cli.impl.command; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.MissingOptionException; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.nifi.toolkit.cli.api.Command; +import org.apache.nifi.toolkit.cli.api.Context; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.Properties; + +/** + * Base class for all commands. + */ +public abstract class AbstractCommand implements Command { + + protected static final ObjectMapper MAPPER = new ObjectMapper(); + static { + MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); + MAPPER.setDefaultPropertyInclusion(JsonInclude.Value.construct(JsonInclude.Include.NON_NULL, JsonInclude.Include.NON_NULL)); + MAPPER.setAnnotationIntrospector(new JaxbAnnotationIntrospector(MAPPER.getTypeFactory())); + MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + protected static final ObjectWriter OBJECT_WRITER = MAPPER.writerWithDefaultPrettyPrinter(); + + private final String name; + private final Options options; + + private Context context; + private PrintStream output; + + public AbstractCommand(final String name) { + this.name = name; + Validate.notNull(this.name); + + this.options = new Options(); + + this.options.addOption(CommandOption.URL.createOption()); + this.options.addOption(CommandOption.PROPERTIES.createOption()); + + this.options.addOption(CommandOption.KEYSTORE.createOption()); + this.options.addOption(CommandOption.KEYSTORE_TYPE.createOption()); + this.options.addOption(CommandOption.KEYSTORE_PASSWORD.createOption()); + this.options.addOption(CommandOption.KEY_PASSWORD.createOption()); + + this.options.addOption(CommandOption.TRUSTSTORE.createOption()); + this.options.addOption(CommandOption.TRUSTSTORE_TYPE.createOption()); + this.options.addOption(CommandOption.TRUSTSTORE_PASSWORD.createOption()); + + this.options.addOption(CommandOption.PROXIED_ENTITY.createOption()); + + this.options.addOption(CommandOption.VERBOSE.createOption()); + this.options.addOption(CommandOption.HELP.createOption()); + } + + @Override + public final void initialize(final Context context) { + Validate.notNull(context); + Validate.notNull(context.getOutput()); + this.context = context; + this.output = context.getOutput(); + this.doInitialize(context); + } + + protected void doInitialize(final Context context) { + // sub-classes can override to do additional things like add options + } + + protected void addOption(final Option option) { + this.options.addOption(option); + } + + protected Context getContext() { + return this.context; + } + + @Override + public String getName() { + return name; + } + + @Override + public Options getOptions() { + return options; + } + + @Override + public void printUsage(String errorMessage) { + output.println(); + + if (errorMessage != null) { + output.println("ERROR: " + errorMessage); + output.println(); + } + + final PrintWriter printWriter = new PrintWriter(output); + + final HelpFormatter hf = new HelpFormatter(); + hf.setWidth(160); + hf.printHelp(printWriter, hf.getWidth(), getName(), null, getOptions(), + hf.getLeftPadding(), hf.getDescPadding(), null, false); + + printWriter.println(); + printWriter.flush(); + } + + + protected void print(final String val) { + output.print(val); + } + + protected void println(final String val) { + output.println(val); + } + + protected void println() { + output.println(); + } + + protected void writeResult(final Properties properties, final Object result) throws IOException { + if (properties.containsKey(CommandOption.OUTPUT_FILE.getLongName())) { + final String outputFile = properties.getProperty(CommandOption.OUTPUT_FILE.getLongName()); + try (final OutputStream resultOut = new FileOutputStream(outputFile)) { + OBJECT_WRITER.writeValue(resultOut, result); + } + } else { + OBJECT_WRITER.writeValue(new OutputStream() { + @Override + public void write(byte[] b) throws IOException { + output.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + output.write(b, off, len); + } + + @Override + public void write(int b) throws IOException { + output.write(b); + } + + @Override + public void close() throws IOException { + // DON'T close the output stream here + output.flush(); + } + }, result); + } + + } + + protected String getArg(final Properties properties, final CommandOption option) { + return properties.getProperty(option.getLongName()); + } + + protected String getRequiredArg(final Properties properties, final CommandOption option) throws MissingOptionException { + final String argValue = properties.getProperty(option.getLongName()); + if (StringUtils.isBlank(argValue)) { + throw new MissingOptionException("Missing required option '" + option.getLongName() + "'"); + } + return argValue; + } + + protected Integer getIntArg(final Properties properties, final CommandOption option) throws MissingOptionException { + final String argValue = properties.getProperty(option.getLongName()); + if (StringUtils.isBlank(argValue)) { + return null; + } + + try { + return Integer.valueOf(argValue); + } catch (Exception e) { + throw new MissingOptionException("Version must be numeric: " + argValue); + } + } + + protected Integer getRequiredIntArg(final Properties properties, final CommandOption option) throws MissingOptionException { + final String argValue = properties.getProperty(option.getLongName()); + if (StringUtils.isBlank(argValue)) { + throw new MissingOptionException("Missing required option '" + option.getLongName() + "'"); + } + + try { + return Integer.valueOf(argValue); + } catch (Exception e) { + throw new MissingOptionException("Version must be numeric: " + argValue); + } + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommandGroup.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommandGroup.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommandGroup.java new file mode 100644 index 0000000..f597365 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommandGroup.java @@ -0,0 +1,73 @@ +/* + * 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.toolkit.cli.impl.command; + +import org.apache.commons.lang3.Validate; +import org.apache.nifi.toolkit.cli.api.Command; +import org.apache.nifi.toolkit.cli.api.CommandGroup; +import org.apache.nifi.toolkit.cli.api.Context; + +import java.io.PrintStream; +import java.util.Collections; +import java.util.List; + +/** + * Base class for CommandGroups to extend from. + */ +public abstract class AbstractCommandGroup implements CommandGroup { + + private final String name; + private PrintStream output; + private List<Command> commands; + + public AbstractCommandGroup(final String name) { + this.name = name; + Validate.notBlank(this.name); + } + + @Override + public final void initialize(final Context context) { + Validate.notNull(context); + this.output = context.getOutput(); + this.commands = Collections.unmodifiableList(createCommands()); + this.commands.stream().forEach(c -> c.initialize(context)); + } + + /** + * Sub-classes override to provide the appropriate commands for the given group. + * + * @return the list of commands for this group + */ + protected abstract List<Command> createCommands(); + + @Override + public String getName() { + return this.name; + } + + @Override + public List<Command> getCommands() { + return this.commands; + } + + @Override + public void printUsage() { + commands.stream().forEach(c -> output.println("\t" + getName() + " " + c.getName())); + output.flush(); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractPropertyCommand.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractPropertyCommand.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractPropertyCommand.java new file mode 100644 index 0000000..fb4dc7f --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractPropertyCommand.java @@ -0,0 +1,95 @@ +/* + * 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.toolkit.cli.impl.command; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.toolkit.cli.api.CommandException; +import org.apache.nifi.toolkit.cli.api.Session; +import org.apache.nifi.toolkit.cli.impl.session.SessionVariables; + +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.Properties; + +/** + * Base class for commands that support loading properties from the session or an argument. + */ +public abstract class AbstractPropertyCommand extends AbstractCommand { + + public AbstractPropertyCommand(String name) { + super(name); + } + + @Override + public void execute(final CommandLine commandLine) throws CommandException { + try { + final Properties properties = new Properties(); + + // start by loading the properties file if it was specified + if (commandLine.hasOption(CommandOption.PROPERTIES.getLongName())) { + final String propertiesFile = commandLine.getOptionValue(CommandOption.PROPERTIES.getLongName()); + if (!StringUtils.isBlank(propertiesFile)) { + try (final InputStream in = new FileInputStream(propertiesFile)) { + properties.load(in); + } + } + } else { + // no properties file was specified so see if there is anything in the session + final SessionVariables sessionVariable = getPropertiesSessionVariable(); + if (sessionVariable != null) { + final Session session = getContext().getSession(); + final String sessionPropsFiles = session.get(sessionVariable.getVariableName()); + if (!StringUtils.isBlank(sessionPropsFiles)) { + try (final InputStream in = new FileInputStream(sessionPropsFiles)) { + properties.load(in); + } + } + } + } + + // add in anything specified on command line, and override anything that was already there + for (final Option option : commandLine.getOptions()) { + final String optValue = option.getValue() == null ? "" : option.getValue(); + properties.setProperty(option.getLongOpt(), optValue); + } + + // delegate to sub-classes + doExecute(properties); + + } catch (CommandException ce) { + throw ce; + } catch (Exception e) { + throw new CommandException("Error executing command '" + getName() + "' : " + e.getMessage(), e); + } + } + + /** + * @return the SessionVariables that specifies the properties file for this command, or null if not supported + */ + protected abstract SessionVariables getPropertiesSessionVariable(); + + /** + * Sub-classes implement specific command logic. + * + * @param properties the properties which represent the arguments + * @throws CommandException if an error occurrs + */ + protected abstract void doExecute(final Properties properties) throws CommandException; + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandFactory.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandFactory.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandFactory.java new file mode 100644 index 0000000..aec0ae4 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandFactory.java @@ -0,0 +1,67 @@ +/* + * 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.toolkit.cli.impl.command; + +import org.apache.nifi.toolkit.cli.api.Command; +import org.apache.nifi.toolkit.cli.api.CommandGroup; +import org.apache.nifi.toolkit.cli.api.Context; +import org.apache.nifi.toolkit.cli.impl.command.misc.Exit; +import org.apache.nifi.toolkit.cli.impl.command.misc.Help; +import org.apache.nifi.toolkit.cli.impl.command.nifi.NiFiCommandGroup; +import org.apache.nifi.toolkit.cli.impl.command.registry.NiFiRegistryCommandGroup; +import org.apache.nifi.toolkit.cli.impl.command.session.SessionCommandGroup; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * Creates and initializes all of the available commands. + */ +public class CommandFactory { + + public static Map<String,Command> createTopLevelCommands(final Context context) { + final List<Command> commandList = new ArrayList<>(); + commandList.add(new Help()); + commandList.add(new Exit()); + + final Map<String,Command> commandMap = new TreeMap<>(); + commandList.stream().forEach(cmd -> { + cmd.initialize(context); + commandMap.put(cmd.getName(), cmd); + }); + + return Collections.unmodifiableMap(commandMap); + } + + public static Map<String,CommandGroup> createCommandGroups(final Context context) { + + final List<CommandGroup> groups = new ArrayList<>(); + groups.add(new NiFiRegistryCommandGroup()); + groups.add(new NiFiCommandGroup()); + groups.add(new SessionCommandGroup()); + + final Map<String,CommandGroup> groupMap = new TreeMap<>(); + groups.stream().forEach(g -> { + g.initialize(context); + groupMap.put(g.getName(), g); + }); + return Collections.unmodifiableMap(groupMap); + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java new file mode 100644 index 0000000..0b14ee3 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java @@ -0,0 +1,102 @@ +/* + * 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.toolkit.cli.impl.command; + +import org.apache.commons.cli.Option; + +/** + * All possible options for commands. + */ +public enum CommandOption { + + // General + URL("u", "baseUrl", "The URL to execute the command against", true), + INPUT_FILE("i", "inputFile", "A file to read as input, must contain full path and filename", true), + OUTPUT_FILE("o", "outputFile", "A file to write output to, must contain full path and filename", true), + PROPERTIES("p", "properties", "A properties file to load arguments from, " + + "command line values will override anything in the properties file, must contain full path to file", true), + + // Registry - Buckets + BUCKET_ID("b", "bucketIdentifier", "A bucket identifier", true), + BUCKET_NAME("bn", "bucketName", "A bucket name", true), + BUCKET_DESC("bd", "bucketDesc", "A bucket description", true), + + // Registry - Flows + FLOW_ID("f", "flowIdentifier", "A flow identifier", true), + FLOW_NAME("fn", "flowName", "A flow name", true), + FLOW_DESC("fd", "flowDesc", "A flow description", true), + FLOW_VERSION("fv", "flowVersion", "A version of a flow", true), + + // NiFi - Registries + REGISTRY_CLIENT_ID("rcid", "registryClientId", "The id of a registry client", true), + REGISTRY_CLIENT_NAME("rcn", "registryClientName", "The name of the registry client", true), + REGISTRY_CLIENT_URL("rcu", "registryClientUrl", "The url of the registry client", true), + REGISTRY_CLIENT_DESC("rcd", "registryClientDesc", "The description of the registry client", true), + + // NiFi - PGs + PG_ID("pgid", "processGroupId", "The id of a process group", true), + PG_NAME("pgn", "processGroupName", "The name of a process group", true), + + // NiFi - Pos + POS_X("px", "posX", "The X coordinate", true), + POS_Y("py", "posY", "The Y coordinate", true), + + // Security related + KEYSTORE("ks", "keystore", "A keystore to use for TLS/SSL connections", true), + KEYSTORE_TYPE("kst", "keystoreType", "The type of key store being used (JKS or PKCS12)", true), + KEYSTORE_PASSWORD("ksp", "keystorePasswd", "The password of the keystore being used", true), + KEY_PASSWORD("kp", "keyPasswd", "The key password of the keystore being used", true), + TRUSTSTORE("ts", "truststore", "A truststore to use for TLS/SSL connections", true), + TRUSTSTORE_TYPE("tst", "truststoreType", "The type of trust store being used (JKS or PKCS12)", true), + TRUSTSTORE_PASSWORD("tsp", "truststorePasswd", "The password of the truststore being used", true), + PROXIED_ENTITY("pe", "proxiedEntity", "The identity of an entity to proxy", true), + PROTOCOL("pro", "protocol", "The security protocol to use, such as TLSv.1.2", true), + + // Miscellaneous + VERBOSE("verbose", "verbose", "Indicates that verbose output should be provided", false), + HELP("h", "help", "Help", false) + ; + + private final String shortName; + private final String longName; + private final String description; + private final boolean hasArg; + + CommandOption(final String shortName, final String longName, final String description, final boolean hasArg) { + this.shortName = shortName; + this.longName = longName; + this.description = description; + this.hasArg = hasArg; + } + + public String getShortName() { + return shortName; + } + + public String getLongName() { + return longName; + } + + public String getDescription() { + return description; + } + + public Option createOption() { + return Option.builder(shortName).longOpt(longName).desc(description).hasArg(hasArg).build(); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java new file mode 100644 index 0000000..b87b80a --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java @@ -0,0 +1,180 @@ +/* + * 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.toolkit.cli.impl.command; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.commons.lang3.Validate; +import org.apache.nifi.toolkit.cli.api.Command; +import org.apache.nifi.toolkit.cli.api.CommandGroup; +import org.apache.nifi.toolkit.cli.api.Context; + +import java.io.PrintStream; +import java.util.Arrays; +import java.util.Map; + +/** + * Takes the arguments from the shell and executes the appropriate command, or prints appropriate usage. + */ +public class CommandProcessor { + + private final Map<String,Command> topLevelCommands; + private final Map<String,CommandGroup> commandGroups; + private final Context context; + private final PrintStream out; + + public CommandProcessor(final Map<String,Command> topLevelCommands, final Map<String,CommandGroup> commandGroups, final Context context) { + this.topLevelCommands = topLevelCommands; + this.commandGroups = commandGroups; + this.context = context; + this.out = context.getOutput(); + Validate.notNull(this.topLevelCommands); + Validate.notNull(this.commandGroups); + Validate.notNull(this.context); + Validate.notNull(this.out); + } + + public void printBasicUsage(String errorMessage) { + out.println(); + + if (errorMessage != null) { + out.println("ERROR: " + errorMessage); + out.println(); + } + + out.println("commands:"); + out.println(); + + commandGroups.entrySet().stream().forEach(e -> e.getValue().printUsage()); + topLevelCommands.keySet().stream().forEach(k -> out.println("\t" + k)); + out.println(); + } + + private CommandLine parseCli(Command command, String[] args) throws ParseException { + final Options options = command.getOptions(); + final CommandLineParser parser = new DefaultParser(); + final CommandLine commandLine = parser.parse(options, args); + + if (commandLine.hasOption(CommandOption.HELP.getLongName())) { + command.printUsage(null); + return null; + } + + return commandLine; + } + + public void process(String[] args) { + if (args == null || args.length == 0 + || (args.length == 1 && CommandOption.HELP.getLongName().equalsIgnoreCase(args[0]))) { + printBasicUsage(null); + return; + } + + final String commandStr = args[0]; + if (topLevelCommands.containsKey(commandStr)) { + processTopLevelCommand(commandStr, args); + } else if (commandGroups.containsKey(commandStr)) { + processGroupCommand(commandStr, args); + } else { + printBasicUsage("Unknown command '" + commandStr + "'"); + return; + } + } + + private void processTopLevelCommand(final String commandStr, final String[] args) { + try { + final Command command = topLevelCommands.get(commandStr); + + final String[] otherArgs = Arrays.copyOfRange(args, 1, args.length, String[].class); + final CommandLine commandLine = parseCli(command, otherArgs); + if (commandLine == null) { + out.println("Unable to parse command line"); + return; + } + + try { + if (otherArgs.length == 1 && CommandOption.HELP.getLongName().equalsIgnoreCase(otherArgs[0])) { + command.printUsage(null); + } else { + command.execute(commandLine); + } + } catch (Exception e) { + command.printUsage(e.getMessage()); + if (commandLine.hasOption(CommandOption.VERBOSE.getLongName())) { + out.println(); + e.printStackTrace(out); + out.println(); + } + } + + } catch (Exception e) { + out.println(); + e.printStackTrace(out); + out.println(); + } + } + + private void processGroupCommand(final String commandGroupStr, final String[] args) { + if (args.length <= 1) { + printBasicUsage("No command provided to " + commandGroupStr); + return; + } + + final String commandStr = args[1]; + final CommandGroup commandGroup = commandGroups.get(commandGroupStr); + final Command command = commandGroup.getCommands().stream().filter(c -> c.getName().equals(commandStr)).findFirst().orElse(null); + + if (command == null) { + printBasicUsage("Unknown command '" + commandGroupStr + " " + commandStr + "'"); + return; + } + + try { + final String[] otherArgs = Arrays.copyOfRange(args, 2, args.length, String[].class); + final CommandLine commandLine = parseCli(command, otherArgs); + if (commandLine == null) { + out.println("Unable to parse command line"); + return; + } + + try { + if (otherArgs.length == 1 && CommandOption.HELP.getLongName().equalsIgnoreCase(otherArgs[0])) { + command.printUsage(null); + } else { + command.execute(commandLine); + } + } catch (Exception e) { + command.printUsage(e.getMessage()); + if (commandLine.hasOption(CommandOption.VERBOSE.getLongName())) { + out.println(); + e.printStackTrace(out); + out.println(); + } + } + + } catch (Exception e) { + out.println(); + e.printStackTrace(out); + out.println(); + } + } + + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Exit.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Exit.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Exit.java new file mode 100644 index 0000000..b10d17a --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Exit.java @@ -0,0 +1,55 @@ +/* + * 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.toolkit.cli.impl.command.misc; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Options; +import org.apache.nifi.toolkit.cli.api.Command; +import org.apache.nifi.toolkit.cli.api.CommandException; +import org.apache.nifi.toolkit.cli.api.Context; + +/** + * Command for exiting the shell. + */ +public class Exit implements Command { + + @Override + public void initialize(final Context context) { + + } + + @Override + public String getName() { + return "exit"; + } + + @Override + public Options getOptions() { + return new Options(); + } + + @Override + public void printUsage(String errorMessage) { + + } + + @Override + public void execute(final CommandLine cli) throws CommandException { + System.exit(0); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Help.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Help.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Help.java new file mode 100644 index 0000000..cf3c558 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/misc/Help.java @@ -0,0 +1,55 @@ +/* + * 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.toolkit.cli.impl.command.misc; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Options; +import org.apache.nifi.toolkit.cli.api.Command; +import org.apache.nifi.toolkit.cli.api.CommandException; +import org.apache.nifi.toolkit.cli.api.Context; + +/** + * Place-holder so "help" shows up in top-level commands. + */ +public class Help implements Command { + + @Override + public void initialize(final Context context) { + + } + + @Override + public String getName() { + return "help"; + } + + @Override + public Options getOptions() { + return new Options(); + } + + @Override + public void printUsage(String errorMessage) { + + } + + @Override + public void execute(final CommandLine cli) throws CommandException { + // nothing to do + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/AbstractNiFiCommand.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/AbstractNiFiCommand.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/AbstractNiFiCommand.java new file mode 100644 index 0000000..cfe3e53 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/AbstractNiFiCommand.java @@ -0,0 +1,74 @@ +/* + * 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.toolkit.cli.impl.command.nifi; + +import org.apache.commons.cli.MissingOptionException; +import org.apache.nifi.toolkit.cli.api.ClientFactory; +import org.apache.nifi.toolkit.cli.api.CommandException; +import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient; +import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException; +import org.apache.nifi.toolkit.cli.impl.command.AbstractPropertyCommand; +import org.apache.nifi.toolkit.cli.impl.session.SessionVariables; +import org.apache.nifi.web.api.dto.RevisionDTO; + +import java.io.IOException; +import java.util.Properties; + +/** + * Base class for all NiFi commands. + */ +public abstract class AbstractNiFiCommand extends AbstractPropertyCommand { + + public AbstractNiFiCommand(final String name) { + super(name); + } + + @Override + protected SessionVariables getPropertiesSessionVariable() { + return SessionVariables.NIFI_CLIENT_PROPS; + } + + @Override + protected void doExecute(final Properties properties) throws CommandException { + final ClientFactory<NiFiClient> clientFactory = getContext().getNiFiClientFactory(); + try (final NiFiClient client = clientFactory.createClient(properties)) { + doExecute(client, properties); + } catch (Exception e) { + throw new CommandException("Error executing command '" + getName() + "' : " + e.getMessage(), e); + } + } + + /** + * Sub-classes implement to perform the desired action using the provided client and properties. + * + * @param client a NiFi client + * @param properties properties for the command + */ + protected abstract void doExecute(final NiFiClient client, final Properties properties) + throws NiFiClientException, IOException, MissingOptionException, CommandException; + + + protected RevisionDTO getInitialRevisionDTO() { + final String clientId = getContext().getSession().getNiFiClientID(); + + final RevisionDTO revisionDTO = new RevisionDTO(); + revisionDTO.setVersion(new Long(0)); + revisionDTO.setClientId(clientId); + return revisionDTO; + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/9cf9e866/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java new file mode 100644 index 0000000..ab9f011 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java @@ -0,0 +1,57 @@ +/* + * 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.toolkit.cli.impl.command.nifi; + +import org.apache.nifi.toolkit.cli.api.Command; +import org.apache.nifi.toolkit.cli.impl.command.AbstractCommandGroup; +import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.CurrentUser; +import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.GetRootId; +import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGGetVars; +import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGImport; +import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGStart; +import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGStop; +import org.apache.nifi.toolkit.cli.impl.command.nifi.registry.CreateRegistryClient; +import org.apache.nifi.toolkit.cli.impl.command.nifi.registry.ListRegistryClients; +import org.apache.nifi.toolkit.cli.impl.command.nifi.registry.UpdateRegistryClient; + +import java.util.ArrayList; +import java.util.List; + +/** + * CommandGroup for NiFi commands. + */ +public class NiFiCommandGroup extends AbstractCommandGroup { + + public NiFiCommandGroup() { + super("nifi"); + } + + @Override + protected List<Command> createCommands() { + final List<AbstractNiFiCommand> commands = new ArrayList<>(); + commands.add(new CurrentUser()); + commands.add(new GetRootId()); + commands.add(new ListRegistryClients()); + commands.add(new CreateRegistryClient()); + commands.add(new UpdateRegistryClient()); + commands.add(new PGImport()); + commands.add(new PGStart()); + commands.add(new PGStop()); + commands.add(new PGGetVars()); + return new ArrayList<>(commands); + } +}
