http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Group.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Group.java
 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Group.java
new file mode 100644
index 0000000..f22dd97
--- /dev/null
+++ 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Group.java
@@ -0,0 +1,263 @@
+/*
+ * 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.authorization;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * A group that users can belong to.
+ */
+public class Group {
+
+    private final String identifier;
+
+    private final String name;
+
+    private final Set<String> users;
+
+    private Group(final Builder builder) {
+        this.identifier = builder.identifier;
+        this.name = builder.name;
+        this.users = Collections.unmodifiableSet(new HashSet<>(builder.users));
+
+        if (this.identifier == null || this.identifier.trim().isEmpty()) {
+            throw new IllegalArgumentException("Identifier can not be null or 
empty");
+        }
+
+        if (this.name == null || this.name.trim().isEmpty()) {
+            throw new IllegalArgumentException("Name can not be null or 
empty");
+        }
+    }
+
+    /**
+     * @return the identifier of the group
+     */
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    /**
+     * @return the name of the group
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * @return an unmodifiable set of user identifiers that belong to this 
group
+     */
+    public Set<String> getUsers() {
+        return users;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+
+        final Group other = (Group) obj;
+        return Objects.equals(this.identifier, other.identifier);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(this.identifier);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("identifier[%s], name[%s]", getIdentifier(), 
getName());
+    }
+
+
+    /**
+     * Builder for creating Groups.
+     */
+    public static class Builder {
+
+        private String identifier;
+        private String name;
+        private Set<String> users = new HashSet<>();
+        private final boolean fromGroup;
+
+        public Builder() {
+            this.fromGroup = false;
+        }
+
+        /**
+         * Initializes the builder with the state of the provided group. When 
using this constructor
+         * the identifier field of the builder can not be changed and will 
result in an IllegalStateException
+         * if attempting to do so.
+         *
+         * @param other the existing access policy to initialize from
+         */
+        public Builder(final Group other) {
+            if (other == null) {
+                throw new IllegalArgumentException("Provided group can not be 
null");
+            }
+
+            this.identifier = other.getIdentifier();
+            this.name = other.getName();
+            this.users.clear();
+            this.users.addAll(other.getUsers());
+            this.fromGroup = true;
+        }
+
+        /**
+         * Sets the identifier of the builder.
+         *
+         * @param identifier the identifier
+         * @return the builder
+         * @throws IllegalStateException if this method is called when this 
builder was constructed from an existing Group
+         */
+        public Builder identifier(final String identifier) {
+            if (fromGroup) {
+                throw new IllegalStateException(
+                        "Identifier can not be changed when initialized from 
an existing group");
+            }
+
+            this.identifier = identifier;
+            return this;
+        }
+
+        /**
+         * Sets the identifier of the builder to a random UUID.
+         *
+         * @return the builder
+         * @throws IllegalStateException if this method is called when this 
builder was constructed from an existing Group
+         */
+        public Builder identifierGenerateRandom() {
+            if (fromGroup) {
+                throw new IllegalStateException(
+                        "Identifier can not be changed when initialized from 
an existing group");
+            }
+
+            this.identifier = UUID.randomUUID().toString();
+            return this;
+        }
+
+        /**
+         * Sets the identifier of the builder with a UUID generated from the 
specified seed string.
+         *
+         * @return the builder
+         * @throws IllegalStateException if this method is called when this 
builder was constructed from an existing Group
+         */
+        public Builder identifierGenerateFromSeed(final String seed) {
+            if (fromGroup) {
+                throw new IllegalStateException(
+                        "Identifier can not be changed when initialized from 
an existing group");
+            }
+            if (seed == null) {
+                throw new IllegalArgumentException("Cannot seed the group 
identifier with a null value.");
+            }
+
+            this.identifier = 
UUID.nameUUIDFromBytes(seed.getBytes(StandardCharsets.UTF_8)).toString();
+            return this;
+        }
+
+        /**
+         * Sets the name of the builder.
+         *
+         * @param name the name
+         * @return the builder
+         */
+        public Builder name(final String name) {
+            this.name = name;
+            return this;
+        }
+
+        /**
+         * Adds all users from the provided set to the builder's set of users.
+         *
+         * @param users a set of users to add
+         * @return the builder
+         */
+        public Builder addUsers(final Set<String> users) {
+            if (users != null) {
+                this.users.addAll(users);
+            }
+            return this;
+        }
+
+        /**
+         * Adds the given user to the builder's set of users.
+         *
+         * @param user the user to add
+         * @return the builder
+         */
+        public Builder addUser(final String user) {
+            if (user != null) {
+                this.users.add(user);
+            }
+            return this;
+        }
+
+        /**
+         * Removes the given user from the builder's set of users.
+         *
+         * @param user the user to remove
+         * @return the builder
+         */
+        public Builder removeUser(final String user) {
+            if (user != null) {
+                this.users.remove(user);
+            }
+            return this;
+        }
+
+        /**
+         * Removes all users from the provided set from the builder's set of 
users.
+         *
+         * @param users the users to remove
+         * @return the builder
+         */
+        public Builder removeUsers(final Set<String> users) {
+            if (users != null) {
+                this.users.removeAll(users);
+            }
+            return this;
+        }
+
+        /**
+         * Clears the builder's set of users so that users is non-null with 
size 0.
+         *
+         * @return the builder
+         */
+        public Builder clearUsers() {
+            this.users.clear();
+            return this;
+        }
+
+        /**
+         * @return a new Group constructed from the state of the builder
+         */
+        public Group build() {
+            return new Group(this);
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/ManagedAuthorizer.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/ManagedAuthorizer.java
 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/ManagedAuthorizer.java
new file mode 100644
index 0000000..da82f4e
--- /dev/null
+++ 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/ManagedAuthorizer.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.authorization;
+
+import 
org.apache.nifi.registry.authorization.exception.AuthorizationAccessException;
+import 
org.apache.nifi.registry.authorization.exception.UninheritableAuthorizationsException;
+
+public interface ManagedAuthorizer extends Authorizer {
+
+    /**
+     * Returns a fingerprint representing the authorizations managed by this 
authorizer. The fingerprint will be
+     * used for comparison to determine if two managed authorizers represent a 
compatible set of users,
+     * groups, and/or policies. Must be non null
+     *
+     * @return the fingerprint for this Authorizer
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    String getFingerprint() throws AuthorizationAccessException;
+
+    /**
+     * Parses the fingerprint and adds any users, groups, and policies to the 
current Authorizer.
+     *
+     * @param fingerprint the fingerprint that was obtained from calling 
getFingerprint() on another Authorizer.
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    void inheritFingerprint(final String fingerprint) throws 
AuthorizationAccessException;
+
+    /**
+     * When the fingerprints are not equal, this method will check if the 
proposed fingerprint is inheritable.
+     * If the fingerprint is an exact match, this method will not be invoked 
as there is nothing to inherit.
+     *
+     * @param proposedFingerprint the proposed fingerprint
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     * @throws UninheritableAuthorizationsException if the proposed 
fingerprint was uninheritable
+     */
+    void checkInheritability(final String proposedFingerprint) throws 
AuthorizationAccessException, UninheritableAuthorizationsException;
+
+    /**
+     * Returns the AccessPolicy provider for this managed Authorizer. Must be 
non null
+     *
+     * @return the AccessPolicy provider
+     */
+    AccessPolicyProvider getAccessPolicyProvider();
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/RequestAction.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/RequestAction.java
 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/RequestAction.java
new file mode 100644
index 0000000..231b9eb
--- /dev/null
+++ 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/RequestAction.java
@@ -0,0 +1,54 @@
+/*
+ * 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.authorization;
+
+import java.util.StringJoiner;
+
+/**
+ * Actions a user/entity can take on a resource.
+ */
+public enum RequestAction {
+    READ("read"),
+    WRITE("write");
+    // TODO, add DELETE RequestAction feature
+
+    private String value;
+
+    RequestAction(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return value.toLowerCase();
+    }
+
+    public static RequestAction valueOfValue(final String action) {
+        if (RequestAction.READ.toString().equals(action)) {
+            return RequestAction.READ;
+        } else if (RequestAction.WRITE.toString().equals(action)) {
+            return RequestAction.WRITE;
+        } else {
+            StringJoiner stringJoiner = new StringJoiner(", ");
+            for(RequestAction ra : RequestAction.values()) {
+                stringJoiner.add(ra.toString());
+            }
+            String allowableValues = stringJoiner.toString();
+            throw new IllegalArgumentException("Action must be one of [" + 
allowableValues + "]");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Resource.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Resource.java
 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Resource.java
new file mode 100644
index 0000000..711f724
--- /dev/null
+++ 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Resource.java
@@ -0,0 +1,44 @@
+/*
+ * 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.authorization;
+
+/**
+ * Resource in an authorization request.
+ */
+public interface Resource {
+
+    /**
+     * The identifier for this resource.
+     *
+     * @return identifier for this resource
+     */
+    String getIdentifier();
+
+    /**
+     * The name of this resource. May be null.
+     *
+     * @return name of this resource
+     */
+    String getName();
+
+    /**
+     * The description of this resource that may be safely used in messages to 
the client.
+     *
+     * @return safe description
+     */
+    String getSafeDescription();
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/User.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/User.java
 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/User.java
new file mode 100644
index 0000000..79f12a8
--- /dev/null
+++ 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/User.java
@@ -0,0 +1,188 @@
+/*
+ * 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.authorization;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * A user to create authorization policies for.
+ */
+public class User {
+
+    private final String identifier;
+
+    private final String identity;
+
+    private User(final Builder builder) {
+        this.identifier = builder.identifier;
+        this.identity = builder.identity;
+
+        if (identifier == null || identifier.trim().isEmpty()) {
+            throw new IllegalArgumentException("Identifier can not be null or 
empty");
+        }
+
+        if (identity == null || identity.trim().isEmpty()) {
+            throw new IllegalArgumentException("Identity can not be null or 
empty");
+        }
+
+    }
+
+    /**
+     * @return the identifier of the user
+     */
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    /**
+     * @return the identity string of the user
+     */
+    public String getIdentity() {
+        return identity;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+
+        final User other = (User) obj;
+        return Objects.equals(this.identifier, other.identifier);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(this.identifier);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("identifier[%s], identity[%s]", getIdentifier(), 
getIdentity());
+    }
+
+    /**
+     * Builder for Users.
+     */
+    public static class Builder {
+
+        private String identifier;
+        private String identity;
+        private final boolean fromUser;
+
+        /**
+         * Default constructor for building a new User.
+         */
+        public Builder() {
+            this.fromUser = false;
+        }
+
+        /**
+         * Initializes the builder with the state of the provided user. When 
using this constructor
+         * the identifier field of the builder can not be changed and will 
result in an IllegalStateException
+         * if attempting to do so.
+         *
+         * @param other the existing user to initialize from
+         */
+        public Builder(final User other) {
+            if (other == null) {
+                throw new IllegalArgumentException("Provided user can not be 
null");
+            }
+
+            this.identifier = other.getIdentifier();
+            this.identity = other.getIdentity();
+            this.fromUser = true;
+        }
+
+        /**
+         * Sets the identifier of the builder.
+         *
+         * @param identifier the identifier to set
+         * @return the builder
+         * @throws IllegalStateException if this method is called when this 
builder was constructed from an existing User
+         */
+        public Builder identifier(final String identifier) {
+            if (fromUser) {
+                throw new IllegalStateException(
+                        "Identifier can not be changed when initialized from 
an existing user");
+            }
+
+            this.identifier = identifier;
+            return this;
+        }
+
+        /**
+         * Sets the identifier of the builder to a random UUID.
+         *
+         * @return the builder
+         * @throws IllegalStateException if this method is called when this 
builder was constructed from an existing User
+         */
+        public Builder identifierGenerateRandom() {
+            if (fromUser) {
+                throw new IllegalStateException(
+                        "Identifier can not be changed when initialized from 
an existing user");
+            }
+
+            this.identifier = UUID.randomUUID().toString();
+            return this;
+        }
+
+        /**
+         * Sets the identifier of the builder with a UUID generated from the 
specified seed string.
+         *
+         * @return the builder
+         * @throws IllegalStateException if this method is called when this 
builder was constructed from an existing User
+         */
+        public Builder identifierGenerateFromSeed(final String seed) {
+            if (fromUser) {
+                throw new IllegalStateException(
+                        "Identifier can not be changed when initialized from 
an existing user");
+            }
+            if (seed == null) {
+                throw new IllegalArgumentException("Cannot seed the user 
identifier with a null value.");
+            }
+
+            this.identifier = 
UUID.nameUUIDFromBytes(seed.getBytes(StandardCharsets.UTF_8)).toString();
+            return this;
+        }
+
+        /**
+         * Sets the identity of the builder.
+         *
+         * @param identity the identity to set
+         * @return the builder
+         */
+        public Builder identity(final String identity) {
+            this.identity = identity;
+            return this;
+        }
+
+        /**
+         * @return a new User constructed from the state of the builder
+         */
+        public User build() {
+            return new User(this);
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserAndGroups.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserAndGroups.java
 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserAndGroups.java
new file mode 100644
index 0000000..b8f150a
--- /dev/null
+++ 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserAndGroups.java
@@ -0,0 +1,40 @@
+/*
+ * 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.authorization;
+
+import java.util.Set;
+
+/**
+ * A holder object to provide atomic access to a user and their groups.
+ */
+public interface UserAndGroups {
+
+    /**
+     * Retrieves the user, or null if the user is unknown
+     *
+     * @return the user with the given identity
+     */
+    User getUser();
+
+    /**
+     * Retrieves the groups for the user, or null if the user is unknown or 
has no groups.
+     *
+     * @return the set of groups for the given user identity
+     */
+    Set<Group> getGroups();
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserContextKeys.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserContextKeys.java
 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserContextKeys.java
new file mode 100644
index 0000000..daac9e8
--- /dev/null
+++ 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserContextKeys.java
@@ -0,0 +1,26 @@
+/*
+ * 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.authorization;
+
+/**
+ * Constants for keys that can be passed in the AuthorizationRequest user 
context Map.
+ */
+public enum UserContextKeys {
+
+    CLIENT_ADDRESS;
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProvider.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProvider.java
 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProvider.java
new file mode 100644
index 0000000..c0460da
--- /dev/null
+++ 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProvider.java
@@ -0,0 +1,108 @@
+/*
+ * 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.authorization;
+
+import 
org.apache.nifi.registry.authorization.exception.AuthorizationAccessException;
+import 
org.apache.nifi.registry.authorization.exception.AuthorizerCreationException;
+import 
org.apache.nifi.registry.authorization.exception.AuthorizerDestructionException;
+
+import java.util.Set;
+
+/**
+ * Provides access to Users and Groups.
+ *
+ * NOTE: Extensions will be called often and frequently. Because of this, if 
the underlying implementation needs to
+ * make remote calls or expensive calculations those should probably be done 
asynchronously and/or cache the results.
+ *
+ * Additionally, extensions need to be thread safe.
+ */
+public interface UserGroupProvider {
+
+    /**
+     * Retrieves all users. Must be non null
+     *
+     * @return a list of users
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    Set<User> getUsers() throws AuthorizationAccessException;
+
+    /**
+     * Retrieves the user with the given identifier.
+     *
+     * @param identifier the id of the user to retrieve
+     * @return the user with the given id, or null if no matching user was 
found
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    User getUser(String identifier) throws AuthorizationAccessException;
+
+    /**
+     * Retrieves the user with the given identity.
+     *
+     * @param identity the identity of the user to retrieve
+     * @return the user with the given identity, or null if no matching user 
was found
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    User getUserByIdentity(String identity) throws 
AuthorizationAccessException;
+
+    /**
+     * Retrieves all groups. Must be non null
+     *
+     * @return a list of groups
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    Set<Group> getGroups() throws AuthorizationAccessException;
+
+    /**
+     * Retrieves a Group by id.
+     *
+     * @param identifier the identifier of the Group to retrieve
+     * @return the Group with the given identifier, or null if no matching 
group was found
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    Group getGroup(String identifier) throws AuthorizationAccessException;
+
+    /**
+     * Gets a user and their groups. Must be non null. If the user is not 
known the UserAndGroups.getUser() and
+     * UserAndGroups.getGroups() should return null
+     *
+     * @return the UserAndGroups for the specified identity
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    UserAndGroups getUserAndGroups(String identity) throws 
AuthorizationAccessException;
+
+    /**
+     * Called immediately after instance creation for implementers to perform 
additional setup
+     *
+     * @param initializationContext in which to initialize
+     */
+    void initialize(UserGroupProviderInitializationContext 
initializationContext) throws AuthorizerCreationException;
+
+    /**
+     * Called to configure the Authorizer.
+     *
+     * @param configurationContext at the time of configuration
+     * @throws AuthorizerCreationException for any issues configuring the 
provider
+     */
+    void onConfigured(AuthorizerConfigurationContext configurationContext) 
throws AuthorizerCreationException;
+
+    /**
+     * Called immediately before instance destruction for implementers to 
release resources.
+     *
+     * @throws AuthorizerDestructionException If pre-destruction fails.
+     */
+    void preDestruction() throws AuthorizerDestructionException;
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProviderInitializationContext.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProviderInitializationContext.java
 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProviderInitializationContext.java
new file mode 100644
index 0000000..6a213fa
--- /dev/null
+++ 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProviderInitializationContext.java
@@ -0,0 +1,37 @@
+/*
+ * 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.authorization;
+
+/**
+ * Initialization content for UserGroupProviders.
+ */
+public interface UserGroupProviderInitializationContext {
+
+    /**
+     * The identifier of the UserGroupProvider.
+     *
+     * @return  The identifier
+     */
+    String getIdentifier();
+
+    /**
+     * The lookup for accessing other configured UserGroupProviders.
+     *
+     * @return  The UserGroupProvider lookup
+     */
+    UserGroupProviderLookup getUserGroupProviderLookup();
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProviderLookup.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProviderLookup.java
 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProviderLookup.java
new file mode 100644
index 0000000..ddf6124
--- /dev/null
+++ 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProviderLookup.java
@@ -0,0 +1,31 @@
+/*
+ * 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.authorization;
+
+/**
+ *
+ */
+public interface UserGroupProviderLookup {
+
+    /**
+     * Looks up the UserGroupProvider with the specified identifier
+     *
+     * @param identifier        The identifier of the UserGroupProvider
+     * @return                  The UserGroupProvider
+     */
+    UserGroupProvider getUserGroupProvider(String identifier);
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AccessDeniedException.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AccessDeniedException.java
 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AccessDeniedException.java
new file mode 100644
index 0000000..7b09a6e
--- /dev/null
+++ 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AccessDeniedException.java
@@ -0,0 +1,39 @@
+/*
+ * 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.authorization.exception;
+
+/**
+ * Represents any error that might occur while authorizing user requests.
+ */
+public class AccessDeniedException extends RuntimeException {
+    private static final long serialVersionUID = -5683444815269084134L;
+
+    public AccessDeniedException(Throwable cause) {
+        super(cause);
+    }
+
+    public AccessDeniedException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public AccessDeniedException(String message) {
+        super(message);
+    }
+
+    public AccessDeniedException() {
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizationAccessException.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizationAccessException.java
 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizationAccessException.java
new file mode 100644
index 0000000..407e182
--- /dev/null
+++ 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizationAccessException.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.authorization.exception;
+
+/**
+ * Represents the case when an authorization decision could not be made 
because the Authorizer was unable to access the underlying data store.
+ */
+public class AuthorizationAccessException extends RuntimeException {
+
+    public AuthorizationAccessException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public AuthorizationAccessException(String message) {
+        super(message);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizerCreationException.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizerCreationException.java
 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizerCreationException.java
new file mode 100644
index 0000000..2a7ae36
--- /dev/null
+++ 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizerCreationException.java
@@ -0,0 +1,39 @@
+/*
+ * 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.authorization.exception;
+
+/**
+ * Represents the exceptional case when an Authorizer fails instantiation.
+ *
+ */
+public class AuthorizerCreationException extends RuntimeException {
+
+    public AuthorizerCreationException() {
+    }
+
+    public AuthorizerCreationException(String msg) {
+        super(msg);
+    }
+
+    public AuthorizerCreationException(Throwable cause) {
+        super(cause);
+    }
+
+    public AuthorizerCreationException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizerDestructionException.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizerDestructionException.java
 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizerDestructionException.java
new file mode 100644
index 0000000..0f4a498
--- /dev/null
+++ 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizerDestructionException.java
@@ -0,0 +1,39 @@
+/*
+ * 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.authorization.exception;
+
+/**
+ * Represents the exceptional case when an Authorizer fails destruction.
+ *
+ */
+public class AuthorizerDestructionException extends RuntimeException {
+
+    public AuthorizerDestructionException() {
+    }
+
+    public AuthorizerDestructionException(String msg) {
+        super(msg);
+    }
+
+    public AuthorizerDestructionException(Throwable cause) {
+        super(cause);
+    }
+
+    public AuthorizerDestructionException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/UninheritableAuthorizationsException.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/UninheritableAuthorizationsException.java
 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/UninheritableAuthorizationsException.java
new file mode 100644
index 0000000..fe110f7
--- /dev/null
+++ 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/UninheritableAuthorizationsException.java
@@ -0,0 +1,28 @@
+/*
+ * 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.authorization.exception;
+
+/**
+ * Represents the case when the proposed authorizations are not inheritable.
+ */
+public class UninheritableAuthorizationsException extends RuntimeException {
+
+    public UninheritableAuthorizationsException(String message) {
+        super(message);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-security/pom.xml b/nifi-registry-security/pom.xml
deleted file mode 100644
index d7711e9..0000000
--- a/nifi-registry-security/pom.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
-    <modelVersion>4.0.0</modelVersion>
-    <parent>
-        <groupId>org.apache.nifi.registry</groupId>
-        <artifactId>nifi-registry</artifactId>
-        <version>0.0.1-SNAPSHOT</version>
-    </parent>
-    <artifactId>nifi-registry-security</artifactId>
-    <packaging>jar</packaging>
-    <build>
-        <resources>
-            <resource>
-                <directory>src/main/xsd</directory>
-            </resource>
-        </resources>
-        <plugins>
-            <plugin>
-                <groupId>org.codehaus.mojo</groupId>
-                <artifactId>jaxb2-maven-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <id>xjc</id>
-                        <goals>
-                            <goal>xjc</goal>
-                        </goals>
-                        <configuration>
-                            
<packageName>org.apache.nifi.registry.user.generated</packageName>
-                        </configuration>
-                    </execution>
-                </executions>
-                <configuration>
-                    
<outputDirectory>${project.build.directory}/generated-sources/jaxb</outputDirectory>
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-checkstyle-plugin</artifactId>
-                <configuration>
-                    <excludes>**/user/generated/*.java</excludes>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
-    <dependencies>
-        <dependency>
-            <groupId>org.apache.nifi.registry</groupId>
-            <artifactId>nifi-registry-properties</artifactId>
-            <version>0.0.1-SNAPSHOT</version>
-        </dependency>
-        <dependency>
-            <groupId>javax.servlet</groupId>
-            <artifactId>javax.servlet-api</artifactId>
-        </dependency>
-    </dependencies>
-</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security/src/main/java/org/apache/nifi/registry/security/AuthorizationProvider.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security/src/main/java/org/apache/nifi/registry/security/AuthorizationProvider.java
 
b/nifi-registry-security/src/main/java/org/apache/nifi/registry/security/AuthorizationProvider.java
deleted file mode 100644
index 89be63e..0000000
--- 
a/nifi-registry-security/src/main/java/org/apache/nifi/registry/security/AuthorizationProvider.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.security;
-
-import org.apache.nifi.registry.properties.NiFiRegistryProperties;
-import org.apache.nifi.registry.user.generated.User;
-import org.apache.nifi.registry.user.generated.Users;
-import org.xml.sax.SAXException;
-
-import javax.xml.XMLConstants;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBElement;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Unmarshaller;
-import javax.xml.transform.stream.StreamSource;
-import javax.xml.validation.Schema;
-import javax.xml.validation.SchemaFactory;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class AuthorizationProvider {
-
-    private static final String USERS_XSD = "/users.xsd";
-    private static final String JAXB_GENERATED_PATH = 
"org.apache.nifi.registry.user.generated";
-    private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext();
-
-    /**
-     * Load the JAXBContext.
-     */
-    private static JAXBContext initializeJaxbContext() {
-        try {
-            return JAXBContext.newInstance(JAXB_GENERATED_PATH, 
AuthorizationProvider.class.getClassLoader());
-        } catch (JAXBException e) {
-            throw new RuntimeException("Unable to create JAXBContext.");
-        }
-    }
-
-    private final List<String> authorizedUsers;
-
-    public AuthorizationProvider(final NiFiRegistryProperties properties) {
-        final File usersFile = properties.getAuthorizedUsersFile();
-        final List<String> userList = new ArrayList<>();
-
-        // load the users from the specified file
-        if (usersFile != null && usersFile.exists()) {
-            try {
-                // find the schema
-                final SchemaFactory schemaFactory = 
SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
-                final Schema schema = 
schemaFactory.newSchema(AuthorizationProvider.class.getResource(USERS_XSD));
-
-                // attempt to unmarshal
-                final Unmarshaller unmarshaller = 
JAXB_CONTEXT.createUnmarshaller();
-                unmarshaller.setSchema(schema);
-                final JAXBElement<Users> element = unmarshaller.unmarshal(new 
StreamSource(usersFile), Users.class);
-                final Users users = element.getValue();
-
-                // add each users dn
-                for (final User user : users.getUser()) {
-                    userList.add(user.getDn());
-                }
-            } catch (SAXException | JAXBException e) {
-                throw new RuntimeException("Unable to read the authorized 
useres file: " + e, e);
-            }
-        }
-
-        authorizedUsers = Collections.unmodifiableList(userList);
-    }
-
-    public boolean canAccess(final String dn) {
-        return authorizedUsers.contains(dn);
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security/src/main/java/org/apache/nifi/registry/security/AuthorizedUserFilter.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security/src/main/java/org/apache/nifi/registry/security/AuthorizedUserFilter.java
 
b/nifi-registry-security/src/main/java/org/apache/nifi/registry/security/AuthorizedUserFilter.java
deleted file mode 100644
index 3276a66..0000000
--- 
a/nifi-registry-security/src/main/java/org/apache/nifi/registry/security/AuthorizedUserFilter.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.security;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.security.cert.X509Certificate;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class AuthorizedUserFilter implements Filter {
-
-    private static final Logger logger = 
LoggerFactory.getLogger(AuthorizedUserFilter.class);
-    private final AuthorizationProvider provider;
-
-    public AuthorizedUserFilter(final AuthorizationProvider provider) {
-        this.provider = provider;
-    }
-
-    @Override
-    public void init(FilterConfig fc) throws ServletException {
-    }
-
-    @Override
-    public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain) throws IOException, ServletException {
-        final HttpServletRequest httpServletRequest = (HttpServletRequest) 
request;
-        final HttpServletResponse httpServletResponse = (HttpServletResponse) 
response;
-
-        if (request.isSecure()) {
-            final String dn = getDn(httpServletRequest);
-
-            // if the user has a certificate, extract the dn and see if they 
can access
-            if (dn != null && provider.canAccess(dn)) {
-                chain.doFilter(request, response);
-            } else {
-                // set the response status
-                httpServletResponse.setContentType("text/plain");
-                
httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
-
-                // write the response message
-                PrintWriter out = httpServletResponse.getWriter();
-                out.println("Access is denied.");
-
-                // log the failure
-                logger.info(String.format(String.format("User <%s> is not 
authorized.", dn)));
-            }
-        } else {
-            chain.doFilter(request, response);
-        }
-    }
-
-    private String getDn(final HttpServletRequest request) {
-        X509Certificate[] certs = (X509Certificate[]) 
request.getAttribute("javax.servlet.request.X509Certificate");
-        if (certs != null && certs.length > 0) {
-            return certs[0].getSubjectDN().getName().trim();
-        } else {
-            return null;
-        }
-    }
-
-    @Override
-    public void destroy() {
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security/src/main/xsd/users.xsd
----------------------------------------------------------------------
diff --git a/nifi-registry-security/src/main/xsd/users.xsd 
b/nifi-registry-security/src/main/xsd/users.xsd
deleted file mode 100644
index fd54c12..0000000
--- a/nifi-registry-security/src/main/xsd/users.xsd
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0"?>
-<!--
-  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.
--->
-<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema";>
-    <!-- user -->
-    <xs:complexType name="User">
-        <xs:attribute name="dn">
-            <xs:simpleType>
-                <xs:restriction base="xs:string">
-                    <xs:minLength value="1"/>
-                    <xs:pattern value=".*[^\s].*"/>
-                </xs:restriction>
-            </xs:simpleType>
-        </xs:attribute>
-    </xs:complexType>
-
-    <!-- users -->
-    <xs:element name="users">
-        <xs:complexType>
-            <xs:sequence>
-                <xs:element name="user" type="User" minOccurs="0" 
maxOccurs="unbounded"/>
-            </xs:sequence>
-        </xs:complexType>
-    </xs:element>
-</xs:schema>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-utils/src/main/java/org/apache/nifi/registry/util/DataUnit.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-utils/src/main/java/org/apache/nifi/registry/util/DataUnit.java 
b/nifi-registry-utils/src/main/java/org/apache/nifi/registry/util/DataUnit.java
new file mode 100644
index 0000000..21aa9a7
--- /dev/null
+++ 
b/nifi-registry-utils/src/main/java/org/apache/nifi/registry/util/DataUnit.java
@@ -0,0 +1,245 @@
+/*
+ * 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.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public enum DataUnit {
+
+    /**
+     * Bytes
+     */
+    B {
+                @Override
+                public double toB(double value) {
+                    return value;
+                }
+
+                @Override
+                public double toKB(double value) {
+                    return value / POWERS[1];
+                }
+
+                @Override
+                public double toMB(double value) {
+                    return value / POWERS[2];
+                }
+
+                @Override
+                public double toGB(double value) {
+                    return value / POWERS[3];
+                }
+
+                @Override
+                public double toTB(double value) {
+                    return value / POWERS[4];
+                }
+
+                @Override
+                public double convert(double sourceSize, DataUnit sourceUnit) {
+                    return sourceUnit.toB(sourceSize);
+                }
+            },
+    /**
+     * Kilobytes
+     */
+    KB {
+                @Override
+                public double toB(double value) {
+                    return value * POWERS[1];
+                }
+
+                @Override
+                public double toKB(double value) {
+                    return value;
+                }
+
+                @Override
+                public double toMB(double value) {
+                    return value / POWERS[1];
+                }
+
+                @Override
+                public double toGB(double value) {
+                    return value / POWERS[2];
+                }
+
+                @Override
+                public double toTB(double value) {
+                    return value / POWERS[3];
+                }
+
+                @Override
+                public double convert(double sourceSize, DataUnit sourceUnit) {
+                    return sourceUnit.toKB(sourceSize);
+                }
+            },
+    /**
+     * Megabytes
+     */
+    MB {
+                @Override
+                public double toB(double value) {
+                    return value * POWERS[2];
+                }
+
+                @Override
+                public double toKB(double value) {
+                    return value * POWERS[1];
+                }
+
+                @Override
+                public double toMB(double value) {
+                    return value;
+                }
+
+                @Override
+                public double toGB(double value) {
+                    return value / POWERS[1];
+                }
+
+                @Override
+                public double toTB(double value) {
+                    return value / POWERS[2];
+                }
+
+                @Override
+                public double convert(double sourceSize, DataUnit sourceUnit) {
+                    return sourceUnit.toMB(sourceSize);
+                }
+            },
+    /**
+     * Gigabytes
+     */
+    GB {
+                @Override
+                public double toB(double value) {
+                    return value * POWERS[3];
+                }
+
+                @Override
+                public double toKB(double value) {
+                    return value * POWERS[2];
+                }
+
+                @Override
+                public double toMB(double value) {
+                    return value * POWERS[1];
+                }
+
+                @Override
+                public double toGB(double value) {
+                    return value;
+                }
+
+                @Override
+                public double toTB(double value) {
+                    return value / POWERS[1];
+                }
+
+                @Override
+                public double convert(double sourceSize, DataUnit sourceUnit) {
+                    return sourceUnit.toGB(sourceSize);
+                }
+            },
+    /**
+     * Terabytes
+     */
+    TB {
+                @Override
+                public double toB(double value) {
+                    return value * POWERS[4];
+                }
+
+                @Override
+                public double toKB(double value) {
+                    return value * POWERS[3];
+                }
+
+                @Override
+                public double toMB(double value) {
+                    return value * POWERS[2];
+                }
+
+                @Override
+                public double toGB(double value) {
+                    return value * POWERS[1];
+                }
+
+                @Override
+                public double toTB(double value) {
+                    return value;
+                }
+
+                @Override
+                public double convert(double sourceSize, DataUnit sourceUnit) {
+                    return sourceUnit.toTB(sourceSize);
+                }
+            };
+
+    public double convert(final double sourceSize, final DataUnit sourceUnit) {
+        throw new AbstractMethodError();
+    }
+
+    public double toB(double size) {
+        throw new AbstractMethodError();
+    }
+
+    public double toKB(double size) {
+        throw new AbstractMethodError();
+    }
+
+    public double toMB(double size) {
+        throw new AbstractMethodError();
+    }
+
+    public double toGB(double size) {
+        throw new AbstractMethodError();
+    }
+
+    public double toTB(double size) {
+        throw new AbstractMethodError();
+    }
+
+    public static final double[] POWERS = {1,
+        1024D,
+        1024 * 1024D,
+        1024 * 1024 * 1024D,
+        1024 * 1024 * 1024 * 1024D};
+
+    public static final String DATA_SIZE_REGEX = 
"(\\d+(?:\\.\\d+)?)\\s*(B|KB|MB|GB|TB)";
+    public static final Pattern DATA_SIZE_PATTERN = 
Pattern.compile(DATA_SIZE_REGEX);
+
+    public static Double parseDataSize(final String value, final DataUnit 
units) {
+        if (value == null) {
+            return null;
+        }
+
+        final Matcher matcher = DATA_SIZE_PATTERN.matcher(value.toUpperCase());
+        if (!matcher.find()) {
+            throw new IllegalArgumentException("Invalid data size: " + value);
+        }
+
+        final String sizeValue = matcher.group(1);
+        final String unitValue = matcher.group(2);
+
+        final DataUnit sourceUnit = DataUnit.valueOf(unitValue);
+        final double size = Double.parseDouble(sizeValue);
+        return units.convert(size, sourceUnit);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-utils/src/main/java/org/apache/nifi/registry/util/FileUtils.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-utils/src/main/java/org/apache/nifi/registry/util/FileUtils.java
 
b/nifi-registry-utils/src/main/java/org/apache/nifi/registry/util/FileUtils.java
index 4c1beed..b7476b9 100644
--- 
a/nifi-registry-utils/src/main/java/org/apache/nifi/registry/util/FileUtils.java
+++ 
b/nifi-registry-utils/src/main/java/org/apache/nifi/registry/util/FileUtils.java
@@ -16,9 +16,19 @@
  */
 package org.apache.nifi.registry.util;
 
+import java.io.Closeable;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.FilenameFilter;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.Collection;
 
@@ -31,8 +41,39 @@ import org.slf4j.Logger;
  */
 public class FileUtils {
 
+    public static final long TRANSFER_CHUNK_SIZE_BYTES = 1024 * 1024 * 8; //8 
MB chunks
     public static final long MILLIS_BETWEEN_ATTEMPTS = 50L;
 
+    /**
+     * Closes the given closeable quietly - no logging, no exceptions...
+     *
+     * @param closeable the thing to close
+     */
+    public static void closeQuietly(final Closeable closeable) {
+        if (null != closeable) {
+            try {
+                closeable.close();
+            } catch (final IOException io) {/*IGNORE*/
+
+            }
+        }
+    }
+
+    /**
+     * Releases the given lock quietly no logging, no exception
+     *
+     * @param lock the lock to release
+     */
+    public static void releaseQuietly(final FileLock lock) {
+        if (null != lock) {
+            try {
+                lock.release();
+            } catch (final IOException io) {
+                /*IGNORE*/
+            }
+        }
+    }
+
     /* Superseded by renamed class bellow */
     @Deprecated
     public static void ensureDirectoryExistAndCanAccess(final File dir) throws 
IOException {
@@ -68,6 +109,140 @@ public class FileUtils {
     }
 
     /**
+     * Copies the given source file to the given destination file. The given 
destination will be overwritten if it already exists.
+     *
+     * @param source the file to copy
+     * @param destination the file to copy to
+     * @param lockInputFile if true will lock input file during copy; if false 
will not
+     * @param lockOutputFile if true will lock output file during copy; if 
false will not
+     * @param move if true will perform what is effectively a move operation 
rather than a pure copy. This allows for potentially highly efficient movement 
of the file but if not possible this will
+     * revert to a copy then delete behavior. If false, then the file is 
copied and the source file is retained. If a true rename/move occurs then no 
lock is held during that time.
+     * @param logger if failures occur, they will be logged to this logger if 
possible. If this logger is null, an IOException will instead be thrown, 
indicating the problem.
+     * @return long number of bytes copied
+     * @throws FileNotFoundException if the source file could not be found
+     * @throws IOException if unable to read or write the underlying streams
+     * @throws SecurityException if a security manager denies the needed file 
operations
+     */
+    public static long copyFile(final File source, final File destination, 
final boolean lockInputFile, final boolean lockOutputFile, final boolean move, 
final Logger logger)
+            throws FileNotFoundException, IOException {
+
+        FileInputStream fis = null;
+        FileOutputStream fos = null;
+        FileLock inLock = null;
+        FileLock outLock = null;
+        long fileSize = 0L;
+        if (!source.canRead()) {
+            throw new IOException("Must at least have read permission");
+
+        }
+        if (move && source.renameTo(destination)) {
+            fileSize = destination.length();
+        } else {
+            try {
+                fis = new FileInputStream(source);
+                fos = new FileOutputStream(destination);
+                final FileChannel in = fis.getChannel();
+                final FileChannel out = fos.getChannel();
+                if (lockInputFile) {
+                    inLock = in.tryLock(0, Long.MAX_VALUE, true);
+                    if (null == inLock) {
+                        throw new IOException("Unable to obtain shared file 
lock for: " + source.getAbsolutePath());
+                    }
+                }
+                if (lockOutputFile) {
+                    outLock = out.tryLock(0, Long.MAX_VALUE, false);
+                    if (null == outLock) {
+                        throw new IOException("Unable to obtain exclusive file 
lock for: " + destination.getAbsolutePath());
+                    }
+                }
+                long bytesWritten = 0;
+                do {
+                    bytesWritten += out.transferFrom(in, bytesWritten, 
TRANSFER_CHUNK_SIZE_BYTES);
+                    fileSize = in.size();
+                } while (bytesWritten < fileSize);
+                out.force(false);
+                FileUtils.closeQuietly(fos);
+                FileUtils.closeQuietly(fis);
+                fos = null;
+                fis = null;
+                if (move && !FileUtils.deleteFile(source, null, 5)) {
+                    if (logger == null) {
+                        FileUtils.deleteFile(destination, null, 5);
+                        throw new IOException("Could not remove file " + 
source.getAbsolutePath());
+                    } else {
+                        logger.warn("Configured to delete source file when 
renaming/move not successful.  However, unable to delete file at: " + 
source.getAbsolutePath());
+                    }
+                }
+            } finally {
+                FileUtils.releaseQuietly(inLock);
+                FileUtils.releaseQuietly(outLock);
+                FileUtils.closeQuietly(fos);
+                FileUtils.closeQuietly(fis);
+            }
+        }
+        return fileSize;
+    }
+
+    /**
+     * Copies the given source file to the given destination file. The given 
destination will be overwritten if it already exists.
+     *
+     * @param source the file to copy from
+     * @param destination the file to copy to
+     * @param lockInputFile if true will lock input file during copy; if false 
will not
+     * @param lockOutputFile if true will lock output file during copy; if 
false will not
+     * @param logger the logger to use
+     * @return long number of bytes copied
+     * @throws FileNotFoundException if the source file could not be found
+     * @throws IOException if unable to read or write to file
+     * @throws SecurityException if a security manager denies the needed file 
operations
+     */
+    public static long copyFile(final File source, final File destination, 
final boolean lockInputFile, final boolean lockOutputFile, final Logger logger) 
throws FileNotFoundException, IOException {
+        return FileUtils.copyFile(source, destination, lockInputFile, 
lockOutputFile, false, logger);
+    }
+
+    public static long copyFile(final File source, final OutputStream stream, 
final boolean closeOutputStream, final boolean lockInputFile) throws 
FileNotFoundException, IOException {
+        FileInputStream fis = null;
+        FileLock inLock = null;
+        long fileSize = 0L;
+        try {
+            fis = new FileInputStream(source);
+            final FileChannel in = fis.getChannel();
+            if (lockInputFile) {
+                inLock = in.tryLock(0, Long.MAX_VALUE, true);
+                if (inLock == null) {
+                    throw new IOException("Unable to obtain exclusive file 
lock for: " + source.getAbsolutePath());
+                }
+
+            }
+
+            byte[] buffer = new byte[1 << 18]; //256 KB
+            int bytesRead = -1;
+            while ((bytesRead = fis.read(buffer)) != -1) {
+                stream.write(buffer, 0, bytesRead);
+            }
+            in.force(false);
+            stream.flush();
+            fileSize = in.size();
+        } finally {
+            FileUtils.releaseQuietly(inLock);
+            FileUtils.closeQuietly(fis);
+            if (closeOutputStream) {
+                FileUtils.closeQuietly(stream);
+            }
+        }
+        return fileSize;
+    }
+
+    public static long copyFile(final InputStream stream, final File 
destination, final boolean closeInputStream, final boolean lockOutputFile) 
throws FileNotFoundException, IOException {
+        final Path destPath = destination.toPath();
+        final long size = Files.copy(stream, destPath);
+        if (closeInputStream) {
+            stream.close();
+        }
+        return size;
+    }
+
+    /**
      * Deletes the given file. If the given file exists but could not be 
deleted
      * this will be printed as a warning to the given logger
      *

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-utils/src/main/java/org/apache/nifi/registry/util/FormatUtils.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-utils/src/main/java/org/apache/nifi/registry/util/FormatUtils.java
 
b/nifi-registry-utils/src/main/java/org/apache/nifi/registry/util/FormatUtils.java
new file mode 100644
index 0000000..c1e353d
--- /dev/null
+++ 
b/nifi-registry-utils/src/main/java/org/apache/nifi/registry/util/FormatUtils.java
@@ -0,0 +1,261 @@
+/*
+ * 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.util;
+
+import java.text.NumberFormat;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class FormatUtils {
+
+    private static final String UNION = "|";
+
+    // for Data Sizes
+    private static final double BYTES_IN_KILOBYTE = 1024;
+    private static final double BYTES_IN_MEGABYTE = BYTES_IN_KILOBYTE * 1024;
+    private static final double BYTES_IN_GIGABYTE = BYTES_IN_MEGABYTE * 1024;
+    private static final double BYTES_IN_TERABYTE = BYTES_IN_GIGABYTE * 1024;
+
+    // for Time Durations
+    private static final String NANOS = join(UNION, "ns", "nano", "nanos", 
"nanosecond", "nanoseconds");
+    private static final String MILLIS = join(UNION, "ms", "milli", "millis", 
"millisecond", "milliseconds");
+    private static final String SECS = join(UNION, "s", "sec", "secs", 
"second", "seconds");
+    private static final String MINS = join(UNION, "m", "min", "mins", 
"minute", "minutes");
+    private static final String HOURS = join(UNION, "h", "hr", "hrs", "hour", 
"hours");
+    private static final String DAYS = join(UNION, "d", "day", "days");
+    private static final String WEEKS = join(UNION, "w", "wk", "wks", "week", 
"weeks");
+
+    private static final String VALID_TIME_UNITS = join(UNION, NANOS, MILLIS, 
SECS, MINS, HOURS, DAYS, WEEKS);
+    public static final String TIME_DURATION_REGEX = "(\\d+)\\s*(" + 
VALID_TIME_UNITS + ")";
+    public static final Pattern TIME_DURATION_PATTERN = 
Pattern.compile(TIME_DURATION_REGEX);
+
+    /**
+     * Formats the specified count by adding commas.
+     *
+     * @param count the value to add commas to
+     * @return the string representation of the given value with commas 
included
+     */
+    public static String formatCount(final long count) {
+        return NumberFormat.getIntegerInstance().format(count);
+    }
+
+    /**
+     * Formats the specified duration in 'mm:ss.SSS' format.
+     *
+     * @param sourceDuration the duration to format
+     * @param sourceUnit the unit to interpret the duration
+     * @return representation of the given time data in minutes/seconds
+     */
+    public static String formatMinutesSeconds(final long sourceDuration, final 
TimeUnit sourceUnit) {
+        final long millis = TimeUnit.MILLISECONDS.convert(sourceDuration, 
sourceUnit);
+
+        final long millisInMinute = TimeUnit.MILLISECONDS.convert(1, 
TimeUnit.MINUTES);
+        final int minutes = (int) (millis / millisInMinute);
+        final long secondsMillisLeft = millis - minutes * millisInMinute;
+
+        final long millisInSecond = TimeUnit.MILLISECONDS.convert(1, 
TimeUnit.SECONDS);
+        final int seconds = (int) (secondsMillisLeft / millisInSecond);
+        final long millisLeft = secondsMillisLeft - seconds * millisInSecond;
+
+        return pad2Places(minutes) + ":" + pad2Places(seconds) + "." + 
pad3Places(millisLeft);
+    }
+
+    /**
+     * Formats the specified duration in 'HH:mm:ss.SSS' format.
+     *
+     * @param sourceDuration the duration to format
+     * @param sourceUnit the unit to interpret the duration
+     * @return representation of the given time data in hours/minutes/seconds
+     */
+    public static String formatHoursMinutesSeconds(final long sourceDuration, 
final TimeUnit sourceUnit) {
+        final long millis = TimeUnit.MILLISECONDS.convert(sourceDuration, 
sourceUnit);
+
+        final long millisInHour = TimeUnit.MILLISECONDS.convert(1, 
TimeUnit.HOURS);
+        final int hours = (int) (millis / millisInHour);
+        final long minutesSecondsMillisLeft = millis - hours * millisInHour;
+
+        return pad2Places(hours) + ":" + 
formatMinutesSeconds(minutesSecondsMillisLeft, TimeUnit.MILLISECONDS);
+    }
+
+    private static String pad2Places(final long val) {
+        return (val < 10) ? "0" + val : String.valueOf(val);
+    }
+
+    private static String pad3Places(final long val) {
+        return (val < 100) ? "0" + pad2Places(val) : String.valueOf(val);
+    }
+
+    /**
+     * Formats the specified data size in human readable format.
+     *
+     * @param dataSize Data size in bytes
+     * @return Human readable format
+     */
+    public static String formatDataSize(final double dataSize) {
+        // initialize the formatter
+        final NumberFormat format = NumberFormat.getNumberInstance();
+        format.setMaximumFractionDigits(2);
+
+        // check terabytes
+        double dataSizeToFormat = dataSize / BYTES_IN_TERABYTE;
+        if (dataSizeToFormat > 1) {
+            return format.format(dataSizeToFormat) + " TB";
+        }
+
+        // check gigabytes
+        dataSizeToFormat = dataSize / BYTES_IN_GIGABYTE;
+        if (dataSizeToFormat > 1) {
+            return format.format(dataSizeToFormat) + " GB";
+        }
+
+        // check megabytes
+        dataSizeToFormat = dataSize / BYTES_IN_MEGABYTE;
+        if (dataSizeToFormat > 1) {
+            return format.format(dataSizeToFormat) + " MB";
+        }
+
+        // check kilobytes
+        dataSizeToFormat = dataSize / BYTES_IN_KILOBYTE;
+        if (dataSizeToFormat > 1) {
+            return format.format(dataSizeToFormat) + " KB";
+        }
+
+        // default to bytes
+        return format.format(dataSize) + " bytes";
+    }
+
+    public static long getTimeDuration(final String value, final TimeUnit 
desiredUnit) {
+        final Matcher matcher = 
TIME_DURATION_PATTERN.matcher(value.toLowerCase());
+        if (!matcher.matches()) {
+            throw new IllegalArgumentException("Value '" + value + "' is not a 
valid Time Duration");
+        }
+
+        final String duration = matcher.group(1);
+        final String units = matcher.group(2);
+        TimeUnit specifiedTimeUnit = null;
+        switch (units.toLowerCase()) {
+            case "ns":
+            case "nano":
+            case "nanos":
+            case "nanoseconds":
+                specifiedTimeUnit = TimeUnit.NANOSECONDS;
+                break;
+            case "ms":
+            case "milli":
+            case "millis":
+            case "milliseconds":
+                specifiedTimeUnit = TimeUnit.MILLISECONDS;
+                break;
+            case "s":
+            case "sec":
+            case "secs":
+            case "second":
+            case "seconds":
+                specifiedTimeUnit = TimeUnit.SECONDS;
+                break;
+            case "m":
+            case "min":
+            case "mins":
+            case "minute":
+            case "minutes":
+                specifiedTimeUnit = TimeUnit.MINUTES;
+                break;
+            case "h":
+            case "hr":
+            case "hrs":
+            case "hour":
+            case "hours":
+                specifiedTimeUnit = TimeUnit.HOURS;
+                break;
+            case "d":
+            case "day":
+            case "days":
+                specifiedTimeUnit = TimeUnit.DAYS;
+                break;
+            case "w":
+            case "wk":
+            case "wks":
+            case "week":
+            case "weeks":
+                final long durationVal = Long.parseLong(duration);
+                return desiredUnit.convert(durationVal, TimeUnit.DAYS)*7;
+        }
+
+        final long durationVal = Long.parseLong(duration);
+        return desiredUnit.convert(durationVal, specifiedTimeUnit);
+    }
+
+    public static String formatUtilization(final double utilization) {
+        return utilization + "%";
+    }
+
+    private static String join(final String delimiter, final String... values) 
{
+        if (values.length == 0) {
+            return "";
+        } else if (values.length == 1) {
+            return values[0];
+        }
+
+        final StringBuilder sb = new StringBuilder();
+        sb.append(values[0]);
+        for (int i = 1; i < values.length; i++) {
+            sb.append(delimiter).append(values[i]);
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Formats nanoseconds in the format:
+     * 3 seconds, 8 millis, 3 nanos - if includeTotalNanos = false,
+     * 3 seconds, 8 millis, 3 nanos (3008000003 nanos) - if includeTotalNanos 
= true
+     *
+     * @param nanos the number of nanoseconds to format
+     * @param includeTotalNanos whether or not to include the total number of 
nanoseconds in parentheses in the returned value
+     * @return a human-readable String that is a formatted representation of 
the given number of nanoseconds.
+     */
+    public static String formatNanos(final long nanos, final boolean 
includeTotalNanos) {
+        final StringBuilder sb = new StringBuilder();
+
+        final long seconds = nanos > 1000000000L ? nanos / 1000000000L : 0L;
+        long millis = nanos > 1000000L ? nanos / 1000000L : 0L;
+        final long nanosLeft = nanos % 1000000L;
+
+        if (seconds > 0) {
+            sb.append(seconds).append(" seconds");
+        }
+        if (millis > 0) {
+            if (seconds > 0) {
+                sb.append(", ");
+                millis -= seconds * 1000L;
+            }
+
+            sb.append(millis).append(" millis");
+        }
+        if (seconds > 0 || millis > 0) {
+            sb.append(", ");
+        }
+        sb.append(nanosLeft).append(" nanos");
+
+        if (includeTotalNanos) {
+            sb.append(" (").append(nanos).append(" nanos)");
+        }
+
+        return sb.toString();
+    }
+}

Reply via email to