class ZTSImpl implements KeyStore, ZTSHandler {

    public TenantDomains getTenantDomains(ResourceContext ctx, String providerDomainName,
            String userName, String roleName, String serviceName) {

        final String caller = ctx.getApiName();
        final String principalDomain = logPrincipalAndGetDomain(ctx);

        validateRequest(ctx.request(), principalDomain, caller);
        validate(providerDomainName, TYPE_DOMAIN_NAME, principalDomain, caller);
        validate(userName, TYPE_ENTITY_NAME, principalDomain, caller);
        if (roleName != null) {
            validate(roleName, TYPE_ENTITY_NAME, principalDomain, caller);
        }
        if (serviceName != null) {
            validate(serviceName, TYPE_SERVICE_NAME, principalDomain, caller);
        }
        
        // for consistent handling of all requests, we're going to convert
        // all incoming object values into lower case since ZMS Server
        // saves all of its object names in lower case
        
        providerDomainName = providerDomainName.toLowerCase();
        setRequestDomain(ctx, providerDomainName);
        if (roleName != null) {
            roleName = roleName.toLowerCase();
        }
        if (serviceName != null) {
            serviceName = serviceName.toLowerCase();
        }
        userName = normalizeDomainAliasUser(userName.toLowerCase());
        
        // first retrieve our domain data object from the cache

        DataCache data = dataStore.getDataCache(providerDomainName);
        if (data == null) {
            setRequestDomain(ctx, ZTSConsts.ZTS_UNKNOWN_DOMAIN);
            throw notFoundError("getTenantDomains: No such provider domain: " + providerDomainName,
                    caller, ZTSConsts.ZTS_UNKNOWN_DOMAIN, principalDomain);
        }

        // if the username does not contain a domain then we'll assume
        // user domain and handle accordingly
        
        if (userName.indexOf('.') == -1) {
            userName = this.userDomain + "." + userName;
        }
        
        roleName = convertEmptyStringToNull(roleName);
        String[] requestedRoleList = null;
        if (roleName != null) {
            requestedRoleList = roleName.split(",");
        }
        
        // process our request and retrieve the roles for the principal
        
        Set<String> roles = new HashSet<>();
        
        dataStore.getAccessibleRoles(data, providerDomainName, userName,
                requestedRoleList, roles, false);
        
        // we are going to process the list and only keep the tenant
        // domains - this is based on the role names since our tenant
        // roles are named: <service>.tenant.<domain>.[<resource_group>.]<action>
        
        Set<String> domainNames = new HashSet<>();
        for (String role : roles) {
            
            String domainName = retrieveTenantDomainName(role, serviceName);
            if (domainName != null) {
                domainNames.add(domainName);
            }
        }
        
        TenantDomains tenantDomains = new TenantDomains();
        tenantDomains.setTenantDomainNames(new ArrayList<>(domainNames));

        return tenantDomains;
    }

    public  ZTSImpl();
    public  ZTSImpl(CloudStore implCloudStore, DataStore implDataStore);

}