Kadmin. Split Kadmin codes into: KadminLocalImpl and KadminRemoteImpl
diff --git a/kerby-kdc/src/main/java/org/apache/kerby/kerberos/kdc/KerbyKdcServer.java b/kerby-kdc/src/main/java/org/apache/kerby/kerberos/kdc/KerbyKdcServer.java
index ac789b5..f2e3ca9 100644
--- a/kerby-kdc/src/main/java/org/apache/kerby/kerberos/kdc/KerbyKdcServer.java
+++ b/kerby-kdc/src/main/java/org/apache/kerby/kerberos/kdc/KerbyKdcServer.java
@@ -21,7 +21,8 @@
 
 import org.apache.kerby.kerberos.kdc.impl.NettyKdcServerImpl;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadminImpl;
 import org.apache.kerby.kerberos.kerb.server.KdcServer;
 import org.apache.kerby.util.OSUtil;
 
@@ -31,7 +32,7 @@
  * The mentioned Kerby KDC server implementation.
  */
 public class KerbyKdcServer extends KdcServer {
-    private Kadmin kadmin;
+    private LocalKadmin kadmin;
     public KerbyKdcServer(File confDir) throws KrbException {
         super(confDir);
         setInnerKdcImpl(new NettyKdcServerImpl(getKdcSetting()));
@@ -44,7 +45,7 @@
     public void init() throws KrbException {
         super.init();
 
-        kadmin = new Kadmin(getKdcSetting(), getIdentityService());
+        kadmin = new LocalKadminImpl(getKdcSetting(), getIdentityService());
 
         kadmin.checkBuiltinPrincipals();
     }
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/AdminHelper.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/AdminHelper.java
index a8a7979..62c38b6 100644
--- a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/AdminHelper.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/AdminHelper.java
@@ -33,9 +33,11 @@
 import java.io.IOException;
 import java.util.Date;
 import java.util.List;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
 
 /**
- * Kadmin utilities.
+ * LocalKadmin utilities.
  */
 public final class AdminHelper {
 
@@ -272,4 +274,35 @@
             identity.setLocked(kOptions.getBooleanOption(KadminOption.LOCKED, false));
         }
     }
+
+    /**
+     * Get all the Pattern for matching from glob string.
+     * The glob string can contain "." "*" and "[]"
+     *
+     * @param globString The glob string for matching
+     * @return pattern
+     * @throws KrbException
+     */
+    static Pattern getPatternFromGlobPatternString(String globString) throws KrbException {
+        if (globString == null || globString.equals("")) {
+            return null;
+        }
+        if (!Pattern.matches("^[0-9A-Za-z._/@*?\\[\\]\\-]+$", globString)) {
+            throw new KrbException("Glob pattern string contains invalid character");
+        }
+
+        String patternString = globString;
+        patternString = patternString.replaceAll("\\.", "\\\\.");
+        patternString = patternString.replaceAll("\\?", ".");
+        patternString = patternString.replaceAll("\\*", ".*");
+        patternString = "^" + patternString + "$";
+
+        Pattern pt;
+        try {
+            pt = Pattern.compile(patternString);
+        } catch (PatternSyntaxException e) {
+            throw new KrbException("Invalid glob pattern string");
+        }
+        return pt;
+    }
 }
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/Kadmin.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/Kadmin.java
index d84de1f..e384257 100644
--- a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/Kadmin.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/Kadmin.java
@@ -21,236 +21,58 @@
 
 import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.common.EncryptionUtil;
-import org.apache.kerby.kerberos.kerb.common.KrbUtil;
-import org.apache.kerby.kerberos.kerb.identity.KrbIdentity;
-import org.apache.kerby.kerberos.kerb.identity.backend.BackendConfig;
-import org.apache.kerby.kerberos.kerb.identity.backend.IdentityBackend;
-import org.apache.kerby.kerberos.kerb.keytab.Keytab;
-import org.apache.kerby.kerberos.kerb.server.KdcConfig;
-import org.apache.kerby.kerberos.kerb.server.KdcSetting;
-import org.apache.kerby.kerberos.kerb.server.KdcUtil;
-import org.apache.kerby.kerberos.kerb.type.base.EncryptionKey;
-import org.apache.kerby.kerberos.kerb.type.base.PrincipalName;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import java.io.File;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
 
 /**
- * Server side admin facilities.
+ * Server side admin facilities from remote, similar to MIT kadmin remote mode.
  */
-public class Kadmin {
-    private static final Logger LOG = LoggerFactory.getLogger(Kadmin.class);
-
-    private final KdcSetting kdcSetting;
-    private final IdentityBackend backend;
-
-    /**
-     * Construct with prepared KdcConfig and BackendConfig.
-     *
-     * @throws org.apache.kerby.kerberos.kerb.KrbException e.
-     * @param kdcConfig     The kdc config
-     * @param backendConfig The backend config
-     */
-    public Kadmin(KdcConfig kdcConfig,
-                  BackendConfig backendConfig) throws KrbException {
-        this.backend = KdcUtil.getBackend(backendConfig);
-        this.kdcSetting = new KdcSetting(kdcConfig, backendConfig);
-    }
-
-    /**
-     * Construct with prepared conf dir.
-     *
-     * @throws org.apache.kerby.kerberos.kerb.KrbException e;
-     * @param confDir The path of conf dir
-     */
-    public Kadmin(File confDir) throws KrbException {
-        KdcConfig tmpKdcConfig = KdcUtil.getKdcConfig(confDir);
-        if (tmpKdcConfig == null) {
-            tmpKdcConfig = new KdcConfig();
-        }
-
-        BackendConfig tmpBackendConfig = KdcUtil.getBackendConfig(confDir);
-        if (tmpBackendConfig == null) {
-            tmpBackendConfig = new BackendConfig();
-        }
-
-        this.kdcSetting = new KdcSetting(tmpKdcConfig, tmpBackendConfig);
-
-        backend = KdcUtil.getBackend(tmpBackendConfig);
-    }
-
-    /**
-     * Construct with prepared KdcSetting and Backend.
-     *
-     * @param kdcSetting The kdc setting
-     * @param backend    The identity backend
-     */
-    public Kadmin(KdcSetting kdcSetting, IdentityBackend backend) {
-        this.kdcSetting = kdcSetting;
-        this.backend = backend;
-    }
-
-    /**
-     * Get the tgs principal name.
-     *
-     * @return The tgs principal name.
-     */
-    private String getTgsPrincipal() {
-        return KrbUtil.makeTgsPrincipal(kdcSetting.getKdcRealm()).getName();
-    }
+public interface Kadmin {
 
     /**
      * Get the kadmin principal name.
      *
      * @return The kadmin principal name.
      */
-    public String getKadminPrincipal() {
-        return KrbUtil.makeKadminPrincipal(kdcSetting.getKdcRealm()).getName();
-    }
-
-    /**
-     * Check the built-in principals, will throw KrbException if not exist.
-     * @throws org.apache.kerby.kerberos.kerb.KrbException e.
-     */
-    public void checkBuiltinPrincipals() throws KrbException {
-        String tgsPrincipal = getTgsPrincipal();
-        String kadminPrincipal = getKadminPrincipal();
-        if (backend.getIdentity(tgsPrincipal) == null
-            || backend.getIdentity(kadminPrincipal) == null) {
-            String errorMsg = "The built-in principals do not exist in backend,"
-                + " please run the kdcinit tool.";
-            LOG.error(errorMsg);
-            throw new KrbException(errorMsg);
-        }
-    }
-
-    /**
-     * Create build-in principals.
-     * @throws org.apache.kerby.kerberos.kerb.KrbException e.
-     */
-    public void createBuiltinPrincipals() throws KrbException {
-        String tgsPrincipal = getTgsPrincipal();
-        if (backend.getIdentity(tgsPrincipal) == null) {
-            addPrincipal(tgsPrincipal);
-        } else {
-            String errorMsg = "The tgs principal already exists in backend.";
-            LOG.error(errorMsg);
-            throw new KrbException(errorMsg);
-        }
-
-        String kadminPrincipal = getKadminPrincipal();
-        if (backend.getIdentity(kadminPrincipal) == null) {
-            addPrincipal(kadminPrincipal);
-        } else {
-            String errorMsg = "The kadmin principal already exists in backend.";
-            LOG.error(errorMsg);
-            throw new KrbException(errorMsg);
-        }
-    }
-
-    /**
-     * Delete build-in principals.
-     * @throws org.apache.kerby.kerberos.kerb.KrbException e.
-     */
-    public void deleteBuiltinPrincipals() throws KrbException {
-        deletePrincipal(getTgsPrincipal());
-        deletePrincipal(getKadminPrincipal());
-    }
-
-    /**
-     * Get kdc config.
-     *
-     * @return The kdc config.
-     */
-    public KdcConfig getKdcConfig() {
-        return kdcSetting.getKdcConfig();
-    }
-
-    /**
-     * Get backend config.
-     *
-     * @return The backend config.
-     */
-    public BackendConfig getBackendConfig() {
-        return kdcSetting.getBackendConfig();
-    }
-
-    /**
-     * Get identity backend.
-     *
-     * @return IdentityBackend
-     */
-    public IdentityBackend getIdentityBackend() {
-        return backend;
-    }
+    String getKadminPrincipal();
 
     /**
      * Add principal to backend.
      *
-     * @throws org.apache.kerby.kerberos.kerb.KrbException e.
      * @param principal The principal to be added into backend
+     * @throws KrbException
      */
-    public void addPrincipal(String principal) throws KrbException {
-        principal = fixPrincipal(principal);
-        addPrincipal(principal, new KOptions());
-    }
+    void addPrincipal(String principal) throws KrbException;
 
     /**
      * Add principal to backend.
      *
-     * @throws org.apache.kerby.kerberos.kerb.KrbException e.
      * @param principal The principal to be added into backend
      * @param kOptions The KOptions with principal info
+     * @throws KrbException
      */
-    public void addPrincipal(String principal, KOptions kOptions)
-            throws KrbException {
-        principal = fixPrincipal(principal);
-        KrbIdentity identity = AdminHelper.createIdentity(principal, kOptions);
-        List<EncryptionKey> keys = EncryptionUtil.generateKeys(
-                getKdcConfig().getEncryptionTypes());
-        identity.addKeys(keys);
-        backend.addIdentity(identity);
-    }
+    void addPrincipal(String principal, KOptions kOptions) throws KrbException;
 
     /**
      * Add principal to backend.
      *
-     * @throws org.apache.kerby.kerberos.kerb.KrbException e.
      * @param principal The principal to be added into backend
      * @param password  The password to create encryption key
+     * @throws KrbException
      */
-    public void addPrincipal(String principal, String password)
-            throws KrbException {
-        principal = fixPrincipal(principal);
-        addPrincipal(principal, password, new KOptions());
-    }
+    void addPrincipal(String principal, String password) throws KrbException;
 
     /**
      * Add principal to backend.
      *
-     * @throws org.apache.kerby.kerberos.kerb.KrbException e.
      * @param principal The principal to be added into backend
      * @param password  The password to create encryption key
      * @param kOptions  The KOptions with principal info
+     * @throws KrbException
      */
-    public void addPrincipal(String principal, String password, KOptions kOptions)
-            throws KrbException {
-        principal = fixPrincipal(principal);
-        KrbIdentity identity = AdminHelper.createIdentity(principal, kOptions);
-        List<EncryptionKey> keys = EncryptionUtil.generateKeys(principal, password,
-                getKdcConfig().getEncryptionTypes());
-        identity.addKeys(keys);
-        backend.addIdentity(identity);
-    }
+    void addPrincipal(String principal, String password,
+                      KOptions kOptions) throws KrbException;
 
     /**
      * Export all the keys of the specified principal into the specified keytab
@@ -258,15 +80,9 @@
      *
      * @param keytabFile The keytab file
      * @param principal The principal name
-     * @throws KrbException e
+     * @throws KrbException
      */
-    public void exportKeytab(File keytabFile, String principal)
-            throws KrbException {
-        principal = fixPrincipal(principal);
-        List<String> principals = new ArrayList<>(1);
-        principals.add(principal);
-        exportKeytab(keytabFile, principals);
-    }
+    void exportKeytab(File keytabFile, String principal) throws KrbException;
 
     /**
      * Export all the keys of the specified principals into the specified keytab
@@ -274,43 +90,18 @@
      *
      * @param keytabFile The keytab file
      * @param principals The principal names
-     * @throws KrbException e
+     * @throws KrbException
      */
-    public void exportKeytab(File keytabFile, List<String> principals)
-            throws KrbException {
-        //Get Identity
-        List<KrbIdentity> identities = new LinkedList<>();
-        for (String principal : principals) {
-            KrbIdentity identity = backend.getIdentity(principal);
-            if (identity == null) {
-                throw new KrbException("Can not find the identity for pincipal "
-                        + principal);
-            }
-            identities.add(identity);
-        }
-
-        AdminHelper.exportKeytab(keytabFile, identities);
-    }
+    void exportKeytab(File keytabFile,
+                      List<String> principals) throws KrbException;
 
     /**
      * Export all identity keys to the specified keytab file.
      *
      * @param keytabFile The keytab file
-     * @throws KrbException e
+     * @throws KrbException
      */
-    public void exportKeytab(File keytabFile) throws KrbException {
-        Keytab keytab = AdminHelper.createOrLoadKeytab(keytabFile);
-
-        Iterable<String> principals = backend.getIdentities();
-        for (String principal : principals) {
-            KrbIdentity identity = backend.getIdentity(principal);
-            if (identity != null) {
-                AdminHelper.exportToKeytab(keytab, identity);
-            }
-        }
-
-        AdminHelper.storeKeytab(keytab, keytabFile);
-    }
+    void exportKeytab(File keytabFile) throws KrbException;
 
     /**
      * Remove all the keys of the specified principal in the specified keytab
@@ -318,13 +109,10 @@
      *
      * @param keytabFile The keytab file
      * @param principal The principal name
-     * @throws KrbException e
+     * @throws KrbException
      */
-    public void removeKeytabEntriesOf(File keytabFile, String principal)
-            throws KrbException {
-        principal = fixPrincipal(principal);
-        AdminHelper.removeKeytabEntriesOf(keytabFile, principal);
-    }
+    void removeKeytabEntriesOf(File keytabFile, String principal)
+            throws KrbException;
 
     /**
      * Remove all the keys of the specified principal with specified kvno
@@ -333,13 +121,10 @@
      * @param keytabFile The keytab file
      * @param principal The principal name
      * @param kvno The kvno
-     * @throws KrbException e
+     * @throws KrbException
      */
-    public void removeKeytabEntriesOf(File keytabFile, String principal, int kvno)
-            throws KrbException {
-        principal = fixPrincipal(principal);
-        AdminHelper.removeKeytabEntriesOf(keytabFile, principal, kvno);
-    }
+    void removeKeytabEntriesOf(File keytabFile, String principal, int kvno)
+            throws KrbException;
 
     /**
      * Remove all the old keys of the specified principal
@@ -347,217 +132,76 @@
      *
      * @param keytabFile The keytab file
      * @param principal The principal name
-     * @throws KrbException e
+     * @throws KrbException
      */
-    public void removeOldKeytabEntriesOf(File keytabFile, String principal)
-            throws KrbException {
-        principal = fixPrincipal(principal);
-        AdminHelper.removeOldKeytabEntriesOf(keytabFile, principal);
-    }
+    void removeOldKeytabEntriesOf(File keytabFile, String principal)
+            throws KrbException;
 
     /**
      * Delete the principal in backend.
      *
      * @param principal The principal to be deleted from backend
-     * @throws KrbException e
+     * @throws KrbException
      */
-    public void deletePrincipal(String principal) throws KrbException {
-        principal = fixPrincipal(principal);
-        backend.deleteIdentity(principal);
-    }
+    void deletePrincipal(String principal) throws KrbException;
 
     /**
      * Modify the principal with KOptions.
      *
      * @param principal The principal to be modified
      * @param kOptions The KOptions with changed principal info
-     * @throws KrbException e
+     * @throws KrbException
      */
-    public void modifyPrincipal(String principal, KOptions kOptions)
-            throws KrbException {
-        principal = fixPrincipal(principal);
-        KrbIdentity identity = backend.getIdentity(principal);
-        if (identity == null) {
-            throw new KrbException("Principal \""
-                    + principal + "\" does not exist.");
-        }
-        AdminHelper.updateIdentity(identity, kOptions);
-        backend.updateIdentity(identity);
-    }
+    void modifyPrincipal(String principal, KOptions kOptions) throws KrbException;
 
     /**
      * Rename the principal.
      *
      * @param oldPrincipalName The original principal name
      * @param newPrincipalName The new principal name
-     * @throws KrbException e
+     * @throws KrbException
      */
-    public void renamePrincipal(String oldPrincipalName, String newPrincipalName)
-            throws KrbException {
-        oldPrincipalName = fixPrincipal(oldPrincipalName);
-        newPrincipalName = fixPrincipal(newPrincipalName);
-        KrbIdentity oldIdentity = backend.getIdentity(newPrincipalName);
-        if (oldIdentity != null) {
-            throw new KrbException("Principal \""
-                    + oldIdentity.getPrincipalName() + "\" is already exist.");
-        }
-        KrbIdentity identity = backend.getIdentity(oldPrincipalName);
-        if (identity == null) {
-            throw new KrbException("Principal \""
-                    + oldPrincipalName + "\" does not exist.");
-        }
-        backend.deleteIdentity(oldPrincipalName);
-
-        identity.setPrincipalName(newPrincipalName);
-        identity.setPrincipal(new PrincipalName(newPrincipalName));
-        backend.addIdentity(identity);
-    }
-
-    /**
-     * Get the identity from backend.
-     *
-     * @param principalName The principal name
-     * @throws KrbException e
-     * @return identity
-     */
-    public KrbIdentity getPrincipal(String principalName) throws KrbException {
-        KrbIdentity identity = backend.getIdentity(principalName);
-        return identity;
-    }
+    void renamePrincipal(String oldPrincipalName,
+                         String newPrincipalName) throws KrbException;
 
     /**
      * Get all the principal names from backend.
      *
-     * @throws KrbException e
      * @return principal list
+     * @throws KrbException
      */
-    public List<String> getPrincipals() throws KrbException {
-        Iterable<String> principalNames = backend.getIdentities();
-        List<String> principalList = new LinkedList<>();
-        Iterator<String> iterator = principalNames.iterator();
-        while (iterator.hasNext()) {
-            principalList.add(iterator.next());
-        }
-        return principalList;
-    }
-
-    /**
-     * Get all the Pattern for matching from glob string. The glob string can contain "." "*" and "[]"
-     *
-     * @param globString The glob string for matching
-     * @throws KrbException e
-     * @return pattern
-     */
-    public Pattern getPatternFromGlobPatternString(String globString) throws KrbException
-    {
-        if (globString == null || globString.equals("")) {
-            return null;
-        }
-        if (!Pattern.matches("^[0-9A-Za-z._/@*?\\[\\]\\-]+$", globString)) {
-            throw new KrbException("Glob pattern string contains invalid character!");
-        }
-        String patternString = globString;
-        patternString = patternString.replaceAll("\\.", "\\\\.");
-        patternString = patternString.replaceAll("\\?", ".");
-        patternString = patternString.replaceAll("\\*", ".*");
-        patternString = "^" + patternString + "$";
-
-        Pattern pt;
-        try {
-            pt = Pattern.compile(patternString);
-        } catch (PatternSyntaxException e) {
-            throw new KrbException("Invalid glob pattern string!");
-        }
-        return pt;
-    }
+    List<String> getPrincipals() throws KrbException;
 
     /**
      * Get all the principal names that meets the pattern
      *
-     * @param pt The pattern for matching
-     * @throws KrbException e
+     * @param globString The glob string for matching
      * @return Principal names
+     * @throws KrbException
      */
-    public List<String> getPrincipalNamesByPattern(Pattern pt) throws KrbException {
-        if (pt == null) {
-            return getPrincipals();
-        }
-
-        Boolean containsAt = pt.pattern().indexOf('@') != -1;
-        List<String> result = new LinkedList<>();
-
-        List<String> principalNames = getPrincipals();
-        for (String principal: principalNames) {
-            String toMatch = containsAt ? principal : principal.split("@")[0];
-            Matcher m = pt.matcher(toMatch);
-            if (m.matches()) {
-                result.add(principal);
-            }
-        }
-        return result;
-    }
+    List<String> getPrincipals(String globString) throws KrbException;
 
     /**
-     * Update the password of specified principal.
+     * Change the password of specified principal.
      *
      * @param principal The principal to be updated password
-     * @param password The new password
-     * @throws KrbException e
+     * @param newPassword The new password
+     * @throws KrbException
      */
-    public void updatePassword(String principal, String password)
-            throws KrbException {
-        principal = fixPrincipal(principal);
-        KrbIdentity identity = backend.getIdentity(principal);
-        if (identity == null) {
-            throw new KrbException("Principal " + principal
-                    + "was not found. Please check the input and try again");
-        }
-        List<EncryptionKey> keys = EncryptionUtil.generateKeys(principal, password,
-                getKdcConfig().getEncryptionTypes());
-        identity.addKeys(keys);
-
-        backend.updateIdentity(identity);
-    }
+    void changePassword(String principal, String newPassword) throws KrbException;
 
     /**
      * Update the random keys of specified principal.
      *
      * @param principal The principal to be updated keys
-     * @throws KrbException e
+     * @throws KrbException
      */
-    public void updateKeys(String principal) throws KrbException {
-        principal = fixPrincipal(principal);
-        KrbIdentity identity = backend.getIdentity(principal);
-        if (identity == null) {
-            throw new KrbException("Principal " + principal
-                    + "was not found. Please check the input and try again");
-        }
-        List<EncryptionKey> keys = EncryptionUtil.generateKeys(
-                getKdcConfig().getEncryptionTypes());
-        identity.addKeys(keys);
-        backend.updateIdentity(identity);
-    }
+    void updateKeys(String principal) throws KrbException;
 
     /**
-     * Stop the backend and release any resources associated.
+     * Release any resources associated.
      *
-     * @throws KrbException e
+     * @throws KrbException
      */
-    public void release() throws KrbException {
-        if (backend != null) {
-            backend.stop();
-        }
-    }
-
-    /**
-     * Fix principal name, making it complete.
-     *
-     * @param principal The principal name
-     */
-    private String fixPrincipal(String principal) {
-        if (!principal.contains("@")) {
-            principal += "@" + kdcSetting.getKdcRealm();
-        }
-        return principal;
-    }
+    void release() throws KrbException;
 }
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/KadminServer.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/KadminServer.java
new file mode 100644
index 0000000..933accf
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/KadminServer.java
@@ -0,0 +1,144 @@
+/**
+ *  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.kerby.kerberos.kerb.admin;
+
+import org.apache.kerby.KOptions;
+import org.apache.kerby.kerberos.kerb.KrbException;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Server side admin facilities for remote, similar to MIT kadmind service.
+ * It uses GSSAPI and XDR to communicate with remote client/kadmin to receive
+ * and perform the requested operations. In this server side, it simply leverages
+ * LocalKadmin to perform the real work.
+ *
+ * TO BE IMPLEMENTED.
+ */
+public class KadminServer implements Kadmin {
+    //private LocalKadmin localKadmin;
+
+    @Override
+    public String getKadminPrincipal() {
+        return null;
+    }
+
+    @Override
+    public void addPrincipal(String principal) throws KrbException {
+
+    }
+
+    @Override
+    public void addPrincipal(String principal,
+                             KOptions kOptions) throws KrbException {
+
+    }
+
+    @Override
+    public void addPrincipal(String principal,
+                             String password) throws KrbException {
+
+    }
+
+    @Override
+    public void addPrincipal(String principal, String password,
+                             KOptions kOptions) throws KrbException {
+
+    }
+
+    @Override
+    public void exportKeytab(File keytabFile,
+                             String principal) throws KrbException {
+
+    }
+
+    @Override
+    public void exportKeytab(File keytabFile,
+                             List<String> principals) throws KrbException {
+
+    }
+
+    @Override
+    public void exportKeytab(File keytabFile) throws KrbException {
+
+    }
+
+    @Override
+    public void removeKeytabEntriesOf(File keytabFile,
+                                      String principal) throws KrbException {
+
+    }
+
+    @Override
+    public void removeKeytabEntriesOf(File keytabFile, String principal,
+                                      int kvno) throws KrbException {
+
+    }
+
+    @Override
+    public void removeOldKeytabEntriesOf(File keytabFile,
+                                         String principal) throws KrbException {
+
+    }
+
+    @Override
+    public void deletePrincipal(String principal) throws KrbException {
+
+    }
+
+    @Override
+    public void modifyPrincipal(String principal,
+                                KOptions kOptions) throws KrbException {
+
+    }
+
+    @Override
+    public void renamePrincipal(String oldPrincipalName,
+                                String newPrincipalName) throws KrbException {
+
+    }
+
+    @Override
+    public List<String> getPrincipals() throws KrbException {
+        return null;
+    }
+
+    @Override
+    public List<String> getPrincipals(String globString) throws KrbException {
+        return null;
+    }
+
+    @Override
+    public void changePassword(String principal,
+                               String newPassword) throws KrbException {
+
+    }
+
+    @Override
+    public void updateKeys(String principal) throws KrbException {
+
+    }
+
+    @Override
+    public void release() throws KrbException {
+
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/LocalKadmin.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/LocalKadmin.java
new file mode 100644
index 0000000..d6fb7b5
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/LocalKadmin.java
@@ -0,0 +1,84 @@
+/**
+ *  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.kerby.kerberos.kerb.admin;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.identity.KrbIdentity;
+import org.apache.kerby.kerberos.kerb.identity.backend.BackendConfig;
+import org.apache.kerby.kerberos.kerb.identity.backend.IdentityBackend;
+import org.apache.kerby.kerberos.kerb.server.KdcConfig;
+
+/**
+ * Server side admin facilities for local, similar to MIT kadmin local mode. It
+ * may be not accurate regarding 'local' because, if the identity backend itself
+ * is supported to be accessed from remote, it won't have to be remote; but if
+ * not, then it must be local to the KDC server bounded with the local backend.
+ *
+ * Note, suitable with Kerby KdcServer based KDCs like Kerby KDC.
+ */
+public interface LocalKadmin extends Kadmin {
+
+    /**
+     * Check the built-in principals, will throw KrbException if not exist.
+     * @throws KrbException
+     */
+    void checkBuiltinPrincipals() throws KrbException;
+
+    /**
+     * Create build-in principals.
+     * @throws KrbException
+     */
+    void createBuiltinPrincipals() throws KrbException;
+
+    /**
+     * Delete build-in principals.
+     * @throws KrbException
+     */
+    void deleteBuiltinPrincipals() throws KrbException;
+
+    /**
+     * Get kdc config.
+     *
+     * @return The kdc config.
+     */
+    KdcConfig getKdcConfig();
+
+    /**
+     * Get backend config.
+     *
+     * @return The backend config.
+     */
+    BackendConfig getBackendConfig();
+
+    /**
+     * Get identity backend.
+     *
+     * @return IdentityBackend
+     */
+    IdentityBackend getIdentityBackend();
+
+    /**
+     * Get the identity from backend.
+     *
+     * @param principalName The principal name
+     * @return identity
+     */
+    KrbIdentity getPrincipal(String principalName) throws KrbException;
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/LocalKadminImpl.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/LocalKadminImpl.java
new file mode 100644
index 0000000..b99e749
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/LocalKadminImpl.java
@@ -0,0 +1,393 @@
+/**
+ *  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.kerby.kerberos.kerb.admin;
+
+import org.apache.kerby.KOptions;
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.common.EncryptionUtil;
+import org.apache.kerby.kerberos.kerb.common.KrbUtil;
+import org.apache.kerby.kerberos.kerb.identity.KrbIdentity;
+import org.apache.kerby.kerberos.kerb.identity.backend.BackendConfig;
+import org.apache.kerby.kerberos.kerb.identity.backend.IdentityBackend;
+import org.apache.kerby.kerberos.kerb.keytab.Keytab;
+import org.apache.kerby.kerberos.kerb.server.KdcConfig;
+import org.apache.kerby.kerberos.kerb.server.KdcSetting;
+import org.apache.kerby.kerberos.kerb.server.KdcUtil;
+import org.apache.kerby.kerberos.kerb.type.base.EncryptionKey;
+import org.apache.kerby.kerberos.kerb.type.base.PrincipalName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * The implementation of server side admin facilities for local mode.
+ */
+public class LocalKadminImpl implements LocalKadmin {
+    private static final Logger LOG = LoggerFactory.getLogger(LocalKadminImpl.class);
+
+    private final KdcSetting kdcSetting;
+    private final IdentityBackend backend;
+
+    /**
+     * Construct with prepared KdcConfig and BackendConfig.
+     *
+     * @param kdcConfig     The kdc config
+     * @param backendConfig The backend config
+     * @throws KrbException
+     */
+    public LocalKadminImpl(KdcConfig kdcConfig,
+                           BackendConfig backendConfig) throws KrbException {
+        this.backend = KdcUtil.getBackend(backendConfig);
+        this.kdcSetting = new KdcSetting(kdcConfig, backendConfig);
+    }
+
+    /**
+     * Construct with prepared conf dir.
+     *
+     * @param confDir The path of conf dir
+     * @throws KrbException
+     */
+    public LocalKadminImpl(File confDir) throws KrbException {
+        KdcConfig tmpKdcConfig = KdcUtil.getKdcConfig(confDir);
+        if (tmpKdcConfig == null) {
+            tmpKdcConfig = new KdcConfig();
+        }
+
+        BackendConfig tmpBackendConfig = KdcUtil.getBackendConfig(confDir);
+        if (tmpBackendConfig == null) {
+            tmpBackendConfig = new BackendConfig();
+        }
+
+        this.kdcSetting = new KdcSetting(tmpKdcConfig, tmpBackendConfig);
+
+        backend = KdcUtil.getBackend(tmpBackendConfig);
+    }
+
+    /**
+     * Construct with prepared KdcSetting and Backend.
+     *
+     * @param kdcSetting The kdc setting
+     * @param backend    The identity backend
+     * @throws KrbException
+     */
+    public LocalKadminImpl(KdcSetting kdcSetting, IdentityBackend backend) {
+        this.kdcSetting = kdcSetting;
+        this.backend = backend;
+    }
+
+    /**
+     * Get the tgs principal name.
+     */
+    private String getTgsPrincipal() {
+        return KrbUtil.makeTgsPrincipal(kdcSetting.getKdcRealm()).getName();
+    }
+
+    @Override
+    public String getKadminPrincipal() {
+        return KrbUtil.makeKadminPrincipal(kdcSetting.getKdcRealm()).getName();
+    }
+
+    @Override
+    public void checkBuiltinPrincipals() throws KrbException {
+        String tgsPrincipal = getTgsPrincipal();
+        String kadminPrincipal = getKadminPrincipal();
+        if (backend.getIdentity(tgsPrincipal) == null
+            || backend.getIdentity(kadminPrincipal) == null) {
+            String errorMsg = "The built-in principals do not exist in backend,"
+                + " please run the kdcinit tool.";
+            LOG.error(errorMsg);
+            throw new KrbException(errorMsg);
+        }
+    }
+
+    @Override
+    public void createBuiltinPrincipals() throws KrbException {
+        String tgsPrincipal = getTgsPrincipal();
+        if (backend.getIdentity(tgsPrincipal) == null) {
+            addPrincipal(tgsPrincipal);
+        } else {
+            String errorMsg = "The tgs principal already exists in backend.";
+            LOG.error(errorMsg);
+            throw new KrbException(errorMsg);
+        }
+
+        String kadminPrincipal = getKadminPrincipal();
+        if (backend.getIdentity(kadminPrincipal) == null) {
+            addPrincipal(kadminPrincipal);
+        } else {
+            String errorMsg = "The kadmin principal already exists in backend.";
+            LOG.error(errorMsg);
+            throw new KrbException(errorMsg);
+        }
+    }
+
+    @Override
+    public void deleteBuiltinPrincipals() throws KrbException {
+        deletePrincipal(getTgsPrincipal());
+        deletePrincipal(getKadminPrincipal());
+    }
+
+    @Override
+    public KdcConfig getKdcConfig() {
+        return kdcSetting.getKdcConfig();
+    }
+
+    @Override
+    public BackendConfig getBackendConfig() {
+        return kdcSetting.getBackendConfig();
+    }
+
+    @Override
+    public IdentityBackend getIdentityBackend() {
+        return backend;
+    }
+
+    @Override
+    public void addPrincipal(String principal) throws KrbException {
+        principal = fixPrincipal(principal);
+        addPrincipal(principal, new KOptions());
+    }
+
+    @Override
+    public void addPrincipal(String principal, KOptions kOptions)
+            throws KrbException {
+        principal = fixPrincipal(principal);
+        KrbIdentity identity = AdminHelper.createIdentity(principal, kOptions);
+        List<EncryptionKey> keys = EncryptionUtil.generateKeys(
+                getKdcConfig().getEncryptionTypes());
+        identity.addKeys(keys);
+        backend.addIdentity(identity);
+    }
+
+    @Override
+    public void addPrincipal(String principal, String password)
+            throws KrbException {
+        principal = fixPrincipal(principal);
+        addPrincipal(principal, password, new KOptions());
+    }
+
+    @Override
+    public void addPrincipal(String principal, String password, KOptions kOptions)
+            throws KrbException {
+        principal = fixPrincipal(principal);
+        KrbIdentity identity = AdminHelper.createIdentity(principal, kOptions);
+        List<EncryptionKey> keys = EncryptionUtil.generateKeys(principal, password,
+                getKdcConfig().getEncryptionTypes());
+        identity.addKeys(keys);
+        backend.addIdentity(identity);
+    }
+
+    @Override
+    public void exportKeytab(File keytabFile, String principal)
+            throws KrbException {
+        principal = fixPrincipal(principal);
+        List<String> principals = new ArrayList<>(1);
+        principals.add(principal);
+        exportKeytab(keytabFile, principals);
+    }
+
+    @Override
+    public void exportKeytab(File keytabFile, List<String> principals)
+            throws KrbException {
+        //Get Identity
+        List<KrbIdentity> identities = new LinkedList<>();
+        for (String principal : principals) {
+            KrbIdentity identity = backend.getIdentity(principal);
+            if (identity == null) {
+                throw new KrbException("Can not find the identity for pincipal "
+                        + principal);
+            }
+            identities.add(identity);
+        }
+
+        AdminHelper.exportKeytab(keytabFile, identities);
+    }
+
+    @Override
+    public void exportKeytab(File keytabFile) throws KrbException {
+        Keytab keytab = AdminHelper.createOrLoadKeytab(keytabFile);
+
+        Iterable<String> principals = backend.getIdentities();
+        for (String principal : principals) {
+            KrbIdentity identity = backend.getIdentity(principal);
+            if (identity != null) {
+                AdminHelper.exportToKeytab(keytab, identity);
+            }
+        }
+
+        AdminHelper.storeKeytab(keytab, keytabFile);
+    }
+
+    @Override
+    public void removeKeytabEntriesOf(File keytabFile, String principal)
+            throws KrbException {
+        principal = fixPrincipal(principal);
+        AdminHelper.removeKeytabEntriesOf(keytabFile, principal);
+    }
+
+    @Override
+    public void removeKeytabEntriesOf(File keytabFile, String principal, int kvno)
+            throws KrbException {
+        principal = fixPrincipal(principal);
+        AdminHelper.removeKeytabEntriesOf(keytabFile, principal, kvno);
+    }
+
+    @Override
+    public void removeOldKeytabEntriesOf(File keytabFile, String principal)
+            throws KrbException {
+        principal = fixPrincipal(principal);
+        AdminHelper.removeOldKeytabEntriesOf(keytabFile, principal);
+    }
+
+    @Override
+    public void deletePrincipal(String principal) throws KrbException {
+        principal = fixPrincipal(principal);
+        backend.deleteIdentity(principal);
+    }
+
+    @Override
+    public void modifyPrincipal(String principal, KOptions kOptions)
+            throws KrbException {
+        principal = fixPrincipal(principal);
+        KrbIdentity identity = backend.getIdentity(principal);
+        if (identity == null) {
+            throw new KrbException("Principal \""
+                    + principal + "\" does not exist.");
+        }
+        AdminHelper.updateIdentity(identity, kOptions);
+        backend.updateIdentity(identity);
+    }
+
+    @Override
+    public void renamePrincipal(String oldPrincipalName, String newPrincipalName)
+            throws KrbException {
+        oldPrincipalName = fixPrincipal(oldPrincipalName);
+        newPrincipalName = fixPrincipal(newPrincipalName);
+        KrbIdentity oldIdentity = backend.getIdentity(newPrincipalName);
+        if (oldIdentity != null) {
+            throw new KrbException("Principal \""
+                    + oldIdentity.getPrincipalName() + "\" is already exist.");
+        }
+        KrbIdentity identity = backend.getIdentity(oldPrincipalName);
+        if (identity == null) {
+            throw new KrbException("Principal \""
+                    + oldPrincipalName + "\" does not exist.");
+        }
+        backend.deleteIdentity(oldPrincipalName);
+
+        identity.setPrincipalName(newPrincipalName);
+        identity.setPrincipal(new PrincipalName(newPrincipalName));
+        backend.addIdentity(identity);
+    }
+
+    @Override
+    public KrbIdentity getPrincipal(String principalName) throws KrbException {
+        KrbIdentity identity = backend.getIdentity(principalName);
+        return identity;
+    }
+
+    @Override
+    public List<String> getPrincipals() throws KrbException {
+        Iterable<String> principalNames = backend.getIdentities();
+        List<String> principalList = new LinkedList<>();
+        Iterator<String> iterator = principalNames.iterator();
+        while (iterator.hasNext()) {
+            principalList.add(iterator.next());
+        }
+        return principalList;
+    }
+
+    @Override
+    public List<String> getPrincipals(String globString) throws KrbException {
+        Pattern pt = AdminHelper.getPatternFromGlobPatternString(globString);
+        if (pt == null) {
+            return getPrincipals();
+        }
+
+        Boolean containsAt = pt.pattern().indexOf('@') != -1;
+        List<String> result = new LinkedList<>();
+
+        List<String> principalNames = getPrincipals();
+        for (String principal: principalNames) {
+            String toMatch = containsAt ? principal : principal.split("@")[0];
+            Matcher m = pt.matcher(toMatch);
+            if (m.matches()) {
+                result.add(principal);
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public void changePassword(String principal,
+                               String newPassword) throws KrbException {
+        principal = fixPrincipal(principal);
+        KrbIdentity identity = backend.getIdentity(principal);
+        if (identity == null) {
+            throw new KrbException("Principal " + principal
+                    + "was not found. Please check the input and try again");
+        }
+        List<EncryptionKey> keys = EncryptionUtil.generateKeys(principal, newPassword,
+                getKdcConfig().getEncryptionTypes());
+        identity.addKeys(keys);
+
+        backend.updateIdentity(identity);
+    }
+
+    @Override
+    public void updateKeys(String principal) throws KrbException {
+        principal = fixPrincipal(principal);
+        KrbIdentity identity = backend.getIdentity(principal);
+        if (identity == null) {
+            throw new KrbException("Principal " + principal
+                    + "was not found. Please check the input and try again");
+        }
+        List<EncryptionKey> keys = EncryptionUtil.generateKeys(
+                getKdcConfig().getEncryptionTypes());
+        identity.addKeys(keys);
+        backend.updateIdentity(identity);
+    }
+
+    @Override
+    public void release() throws KrbException {
+        if (backend != null) {
+            backend.stop();
+        }
+    }
+
+    /**
+     * Fix principal name, making it complete.
+     *
+     * @param principal The principal name
+     */
+    private String fixPrincipal(String principal) {
+        if (!principal.contains("@")) {
+            principal += "@" + kdcSetting.getKdcRealm();
+        }
+        return principal;
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/RemoteKadminImpl.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/RemoteKadminImpl.java
new file mode 100644
index 0000000..16115d8
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/RemoteKadminImpl.java
@@ -0,0 +1,144 @@
+/**
+ *  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.kerby.kerberos.kerb.admin;
+
+import org.apache.kerby.KOptions;
+import org.apache.kerby.kerberos.kerb.KrbException;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Server side admin facilities from remote, similar to MIT Kadmin remote mode.
+ * It uses GSSAPI and XDR to communicate with remote KDC/kadmind to do the
+ * requested operations. In the client side, it simply wraps and sends the
+ * request info to the server kadmind side, and then unwraps the response for
+ * the operation result.
+ *
+ * TO BE IMPLEMENTED.
+ */
+public class RemoteKadminImpl implements Kadmin {
+
+    @Override
+    public String getKadminPrincipal() {
+        return null;
+    }
+
+    @Override
+    public void addPrincipal(String principal) throws KrbException {
+
+    }
+
+    @Override
+    public void addPrincipal(String principal,
+                             KOptions kOptions) throws KrbException {
+
+    }
+
+    @Override
+    public void addPrincipal(String principal,
+                             String password) throws KrbException {
+
+    }
+
+    @Override
+    public void addPrincipal(String principal, String password,
+                             KOptions kOptions) throws KrbException {
+
+    }
+
+    @Override
+    public void exportKeytab(File keytabFile,
+                             String principal) throws KrbException {
+
+    }
+
+    @Override
+    public void exportKeytab(File keytabFile,
+                             List<String> principals) throws KrbException {
+
+    }
+
+    @Override
+    public void exportKeytab(File keytabFile) throws KrbException {
+
+    }
+
+    @Override
+    public void removeKeytabEntriesOf(File keytabFile,
+                                      String principal) throws KrbException {
+
+    }
+
+    @Override
+    public void removeKeytabEntriesOf(File keytabFile, String principal,
+                                      int kvno) throws KrbException {
+
+    }
+
+    @Override
+    public void removeOldKeytabEntriesOf(File keytabFile,
+                                         String principal) throws KrbException {
+
+    }
+
+    @Override
+    public void deletePrincipal(String principal) throws KrbException {
+
+    }
+
+    @Override
+    public void modifyPrincipal(String principal,
+                                KOptions kOptions) throws KrbException {
+
+    }
+
+    @Override
+    public void renamePrincipal(String oldPrincipalName,
+                                String newPrincipalName) throws KrbException {
+
+    }
+
+    @Override
+    public List<String> getPrincipals() throws KrbException {
+        return null;
+    }
+
+    @Override
+    public List<String> getPrincipals(String globString) throws KrbException {
+        return null;
+    }
+
+    @Override
+    public void changePassword(String principal,
+                               String newPassword) throws KrbException {
+
+    }
+
+    @Override
+    public void updateKeys(String principal) throws KrbException {
+
+    }
+
+    @Override
+    public void release() throws KrbException {
+
+    }
+}
diff --git a/kerby-kerb/kerb-simplekdc/src/main/java/org/apache/kerby/kerberos/kerb/server/SimpleKdcServer.java b/kerby-kerb/kerb-simplekdc/src/main/java/org/apache/kerby/kerberos/kerb/server/SimpleKdcServer.java
index 5e83207..6f4fd63 100644
--- a/kerby-kerb/kerb-simplekdc/src/main/java/org/apache/kerby/kerberos/kerb/server/SimpleKdcServer.java
+++ b/kerby-kerb/kerb-simplekdc/src/main/java/org/apache/kerby/kerberos/kerb/server/SimpleKdcServer.java
@@ -20,7 +20,8 @@
 package org.apache.kerby.kerberos.kerb.server;
 
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadminImpl;
 import org.apache.kerby.kerberos.kerb.client.Krb5Conf;
 import org.apache.kerby.kerberos.kerb.client.KrbClient;
 import org.apache.kerby.kerberos.kerb.client.KrbPkinitClient;
@@ -36,7 +37,7 @@
  */
 public class SimpleKdcServer extends KdcServer {
     private final KrbClient krbClnt;
-    private Kadmin kadmin;
+    private LocalKadmin kadmin;
     private Krb5Conf krb5Conf;
     private File workDir;
 
@@ -128,7 +129,7 @@
     public void init() throws KrbException {
         super.init();
 
-        kadmin = new Kadmin(getKdcSetting(), getIdentityService());
+        kadmin = new LocalKadminImpl(getKdcSetting(), getIdentityService());
 
         kadmin.createBuiltinPrincipals();
 
@@ -182,7 +183,7 @@
      * Get Kadmin operation interface.
      * @return Kadmin
      */
-    public Kadmin getKadmin() {
+    public LocalKadmin getKadmin() {
         return kadmin;
     }
 
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/KadminTool.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/KadminTool.java
index 34b75b4..970f7cb 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/KadminTool.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/KadminTool.java
@@ -21,7 +21,8 @@
 
 import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadminImpl;
 import org.apache.kerby.kerberos.kerb.admin.KadminOption;
 import org.apache.kerby.kerberos.tool.kadmin.command.AddPrincipalCommand;
 import org.apache.kerby.kerberos.tool.kadmin.command.ChangePasswordCommand;
@@ -102,7 +103,7 @@
         System.exit(-1);
     }
 
-    private static void execute(Kadmin kadmin, String command) {
+    private static void execute(LocalKadmin kadmin, String command) {
         //Omit the leading and trailing whitespace.
         command = command.trim();
         if (command.equals("list_requests")
@@ -182,9 +183,9 @@
             return;
         }
 
-        Kadmin kadmin;
+        LocalKadmin kadmin;
         try {
-            kadmin = new Kadmin(getConfDir(args));
+            kadmin = new LocalKadminImpl(getConfDir(args));
         } catch (KrbException e) {
             System.err.println("Failed to init Kadmin due to " + e.getMessage());
             return;
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/AddPrincipalCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/AddPrincipalCommand.java
index f19d65c..e2374bd 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/AddPrincipalCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/AddPrincipalCommand.java
@@ -21,7 +21,7 @@
 
 import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
 import org.apache.kerby.kerberos.kerb.admin.KadminOption;
 import org.apache.kerby.kerberos.tool.kadmin.ToolUtil;
 
@@ -53,7 +53,7 @@
 
     private KOptions kOptions;
 
-    public AddPrincipalCommand(Kadmin kadmin) {
+    public AddPrincipalCommand(LocalKadmin kadmin) {
         super(kadmin);
     }
 
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ChangePasswordCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ChangePasswordCommand.java
index 1583566..f3d2f45 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ChangePasswordCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ChangePasswordCommand.java
@@ -21,7 +21,7 @@
 
 import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
 import org.apache.kerby.kerberos.kerb.admin.KadminOption;
 import org.apache.kerby.kerberos.tool.kadmin.ToolUtil;
 
@@ -35,7 +35,7 @@
 
     private KOptions kOptions;
 
-    public ChangePasswordCommand(Kadmin kadmin) {
+    public ChangePasswordCommand(LocalKadmin kadmin) {
         super(kadmin);
     }
 
@@ -57,7 +57,7 @@
                 return;
             }
             try {
-                getKadmin().updatePassword(principal, password);
+                getKadmin().changePassword(principal, password);
                 System.out.println("Update password success.");
             } catch (KrbException e) {
                 System.err.println("Fail to update password. " + e.getCause());
@@ -71,7 +71,7 @@
             if (kOptions.contains(KadminOption.PW)) {
                 password = kOptions.getStringOption(KadminOption.PW);
                 try {
-                    getKadmin().updatePassword(principal, password);
+                    getKadmin().changePassword(principal, password);
                     System.out.println("Update password success.");
                 } catch (KrbException e) {
                     System.err.println("Fail to update password. " + e.getMessage());
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/DeletePrincipalCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/DeletePrincipalCommand.java
index feb0534..8322b7b 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/DeletePrincipalCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/DeletePrincipalCommand.java
@@ -21,6 +21,7 @@
 
 import org.apache.kerby.kerberos.kerb.KrbException;
 import org.apache.kerby.kerberos.kerb.admin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
 
 import java.io.Console;
 import java.util.Scanner;
@@ -34,7 +35,7 @@
 
     private Boolean force = false;
 
-    public DeletePrincipalCommand(Kadmin kadmin) {
+    public DeletePrincipalCommand(LocalKadmin kadmin) {
         super(kadmin);
     }
 
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/GetPrincipalCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/GetPrincipalCommand.java
index 33d4929..6c4501f 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/GetPrincipalCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/GetPrincipalCommand.java
@@ -20,7 +20,7 @@
 package org.apache.kerby.kerberos.tool.kadmin.command;
 
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
 import org.apache.kerby.kerberos.kerb.identity.KrbIdentity;
 import org.apache.kerby.kerberos.kerb.type.base.EncryptionKey;
 import org.apache.kerby.kerberos.kerb.type.base.EncryptionType;
@@ -31,7 +31,7 @@
     private static final String USAGE = "Usage: getprinc principalName\n"
             + "such as, getprinc hello@TEST.COM";
 
-    public GetPrincipalCommand(Kadmin kadmin) {
+    public GetPrincipalCommand(LocalKadmin kadmin) {
         super(kadmin);
     }
 
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KadminCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KadminCommand.java
index b93537c..53890e2 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KadminCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KadminCommand.java
@@ -19,17 +19,17 @@
  */
 package org.apache.kerby.kerberos.tool.kadmin.command;
 
-import org.apache.kerby.kerberos.kerb.admin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
 
 public abstract class KadminCommand {
 
-    private Kadmin kadmin;
+    private LocalKadmin kadmin;
 
-    public KadminCommand(Kadmin kadmin) {
+    public KadminCommand(LocalKadmin kadmin) {
         this.kadmin = kadmin;
     }
 
-    protected Kadmin getKadmin() {
+    protected LocalKadmin getKadmin() {
         return kadmin;
     }
 
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KeytabAddCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KeytabAddCommand.java
index 78aaec4..65802f4 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KeytabAddCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KeytabAddCommand.java
@@ -20,11 +20,10 @@
 package org.apache.kerby.kerberos.tool.kadmin.command;
 
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
 
 import java.io.File;
 import java.util.List;
-import java.util.regex.Pattern;
 
 public class KeytabAddCommand extends KadminCommand {
     private static final String USAGE =
@@ -32,7 +31,7 @@
 
     private static final String DEFAULT_KEYTAB_FILE_LOCATION = "/etc/krb5.keytab";
 
-    public KeytabAddCommand(Kadmin kadmin) {
+    public KeytabAddCommand(LocalKadmin kadmin) {
         super(kadmin);
     }
 
@@ -77,8 +76,7 @@
 
         try {
             if (glob) {
-                Pattern pt = getKadmin().getPatternFromGlobPatternString(principal);
-                List<String> principals = getKadmin().getPrincipalNamesByPattern(pt);
+                List<String> principals = getKadmin().getPrincipals(principal);
                 if (principals.size() != 0) {
                     getKadmin().exportKeytab(keytabFile, principals);
                 }
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KeytabRemoveCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KeytabRemoveCommand.java
index 324ef29..d1d9df4 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KeytabRemoveCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KeytabRemoveCommand.java
@@ -21,7 +21,7 @@
 
 import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
 import org.apache.kerby.kerberos.kerb.admin.KadminOption;
 import org.apache.kerby.kerberos.tool.kadmin.ToolUtil;
 
@@ -33,7 +33,7 @@
 
     private static final String DEFAULT_KEYTAB_FILE = "/etc/krb5.keytab";
 
-    public KeytabRemoveCommand(Kadmin kadmin) {
+    public KeytabRemoveCommand(LocalKadmin kadmin) {
         super(kadmin);
     }
 
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ListPrincipalCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ListPrincipalCommand.java
index 387771c..71d909f 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ListPrincipalCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ListPrincipalCommand.java
@@ -20,16 +20,15 @@
 package org.apache.kerby.kerberos.tool.kadmin.command;
 
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
 
 import java.util.List;
-import java.util.regex.Pattern;
 
 public class ListPrincipalCommand extends KadminCommand {
     private static final String USAGE = "Usage: list_principals [expression]\n"
             + "\t'expression' is a shell-style glob expression that can contain the wild-card characters ?, *, and [].";
 
-    public ListPrincipalCommand(Kadmin kadmin) {
+    public ListPrincipalCommand(LocalKadmin kadmin) {
         super(kadmin);
     }
 
@@ -40,8 +39,7 @@
         if (commands.length <= 2) {
             String expression = commands.length == 2 ? commands[1] : null;
             try {
-                Pattern pt = getKadmin().getPatternFromGlobPatternString(expression);
-                List<String> principalNames = getKadmin().getPrincipalNamesByPattern(pt);
+                List<String> principalNames = getKadmin().getPrincipals(expression);
                 if (principalNames.size() == 0) {
                     return;
                 }
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ModifyPrincipalCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ModifyPrincipalCommand.java
index 5d15e28..4d0d16b 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ModifyPrincipalCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ModifyPrincipalCommand.java
@@ -22,7 +22,7 @@
 import org.apache.kerby.KOptionType;
 import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
 import org.apache.kerby.kerberos.kerb.admin.KadminOption;
 import org.apache.kerby.kerberos.tool.kadmin.ToolUtil;
 
@@ -39,7 +39,7 @@
     private KOptions kOptions;
     private String principal;
 
-    public ModifyPrincipalCommand(Kadmin kadmin) {
+    public ModifyPrincipalCommand(LocalKadmin kadmin) {
         super(kadmin);
         this.kOptions = new KOptions();
     }
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/RenamePrincipalCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/RenamePrincipalCommand.java
index 32ac090..80d6785 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/RenamePrincipalCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/RenamePrincipalCommand.java
@@ -22,6 +22,7 @@
 import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
 import org.apache.kerby.kerberos.kerb.admin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
 import org.apache.kerby.kerberos.kerb.admin.KadminOption;
 import org.apache.kerby.kerberos.tool.kadmin.ToolUtil;
 
@@ -33,7 +34,7 @@
     private String oldPrincipalName;
     private String newPrincipalName;
 
-    public RenamePrincipalCommand(Kadmin kadmin) {
+    public RenamePrincipalCommand(LocalKadmin kadmin) {
         super(kadmin);
     }
 
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kdcinit/KdcInitTool.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kdcinit/KdcInitTool.java
index 13a83eb..958f559 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kdcinit/KdcInitTool.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kdcinit/KdcInitTool.java
@@ -20,7 +20,8 @@
 package org.apache.kerby.kerberos.tool.kdcinit;
 
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
+import org.apache.kerby.kerberos.kerb.admin.LocalKadminImpl;
 import org.apache.kerby.util.OSUtil;
 
 import java.io.File;
@@ -29,7 +30,7 @@
  * A tool to initialize KDC backend for the first time when setup the KDC.
  */
 public class KdcInitTool {
-    private Kadmin kadmin;
+    private LocalKadmin kadmin;
     private static File keytabFile;
 
     private static  final String USAGE = (OSUtil.isWindows()
@@ -44,7 +45,7 @@
             + " conf admin.keytab\n";
 
     void initKdc(File confDir) throws KrbException {
-        kadmin = new Kadmin(confDir);
+        kadmin = new LocalKadminImpl(confDir);
         try {
             kadmin.createBuiltinPrincipals();
             kadmin.exportKeytab(keytabFile, kadmin.getKadminPrincipal());