AdobeDPSClient.java 14.9 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  com.adobe.dps.client.authentication.ImsDao
 *  com.adobe.dps.client.authentication.User
 *  com.adobe.dps.client.producer.utils.AccessToken
 *  com.adobe.dps.client.utils.Config
 *  com.adobe.dps.client.utils.Config$Environment
 *  com.adobe.granite.crypto.CryptoException
 *  com.adobe.granite.crypto.CryptoSupport
 *  com.day.cq.commons.inherit.InheritanceValueMap
 *  com.day.cq.wcm.webservicesupport.Configuration
 *  org.apache.felix.scr.annotations.Activate
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Deactivate
 *  org.apache.felix.scr.annotations.Property
 *  org.apache.felix.scr.annotations.Reference
 *  org.apache.felix.scr.annotations.Service
 *  org.apache.http.impl.client.HttpClientBuilder
 *  org.apache.http.osgi.services.HttpClientBuilderFactory
 *  org.apache.sling.commons.osgi.OsgiUtil
 *  org.apache.sling.commons.threads.ModifiableThreadPoolConfig
 *  org.apache.sling.commons.threads.ThreadPool
 *  org.apache.sling.commons.threads.ThreadPoolConfig
 *  org.apache.sling.commons.threads.ThreadPoolConfig$ThreadPriority
 *  org.apache.sling.commons.threads.ThreadPoolManager
 *  org.osgi.service.component.ComponentContext
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.adobe.cq.mobile.dps.impl.service;

import com.adobe.cq.mobile.dps.DPSException;
import com.adobe.dps.client.authentication.ImsDao;
import com.adobe.dps.client.authentication.User;
import com.adobe.dps.client.producer.utils.AccessToken;
import com.adobe.dps.client.utils.Config;
import com.adobe.granite.crypto.CryptoException;
import com.adobe.granite.crypto.CryptoSupport;
import com.day.cq.commons.inherit.InheritanceValueMap;
import com.day.cq.wcm.webservicesupport.Configuration;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.osgi.services.HttpClientBuilderFactory;
import org.apache.sling.commons.osgi.OsgiUtil;
import org.apache.sling.commons.threads.ModifiableThreadPoolConfig;
import org.apache.sling.commons.threads.ThreadPool;
import org.apache.sling.commons.threads.ThreadPoolConfig;
import org.apache.sling.commons.threads.ThreadPoolManager;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(metatype=1, label="Experience Manager Mobile On-demand Services Client", description="Configuration for the Experience Manager Mobile On-demand Services Client")
@Service(value={AdobeDPSClient.class})
public class AdobeDPSClient {
    public static final String AEM_CLIENT_VERSION = "AEM.6.2-CQ327235";
    private static final Logger LOGGER = LoggerFactory.getLogger(AdobeDPSClient.class);
    @Property(label="Client Id", description="Identifies your AEM installation as a client to Experience Manager Mobile On-demand Services", passwordValue={""})
    private static final String PROPERTY_CLIENT_ID = "clientId";
    private String publishClientId = null;
    @Property(label="Client Secret", description="Used to authenticate and authorise the AEM installation as a client to Experience Manager Mobile On-demand Services", passwordValue={""})
    private static final String PROPERTY_CLIENT_SECRET = "clientSecret";
    private String publishClientSecret = null;
    @Property(label="Minimum thread pool size", description="Minimum thread pool size for AEM to Experience Manager Mobile On-demand Services requests", intValue={10})
    private static final String MINIMUM_THREAD_POOL_SIZE = "minThreadPoolSize";
    private int minThreadPoolSize;
    @Property(label="Maximum thread pool size", description="Maximum thread pool size for AEM to Experience Manager Mobile On-demand Services requests", intValue={20})
    private static final String MAXIMUM_THREAD_POOL_SIZE = "maxThreadPoolSize";
    private int maxThreadPoolSize;
    @Property(label="Access token cache expiry", description="Minutes to keep an access token", intValue={30})
    private static final String ACCESS_TOKEN_EXPIRY_IN_MINS = "accessTokenExpiryInMinutes";
    private int accessTokenExpiryInMins;
    @Property(label="Environment", description="Experience Manager Mobile On-demand Services Cloud Environment, leave blank unless access was granted in other environments")
    private static final String PROPERTY_ENVIRONMENT = "environment";
    private String publishEnvironment;
    @Reference
    private ThreadPoolManager threadPoolManager;
    @Reference
    private CryptoSupport cryptoSupportService;
    @Reference
    private HttpClientBuilderFactory httpClientBuilderFactory;
    private ThreadPool threadPool;
    private Properties httpProperties = null;
    private static Map<String, AccessToken> accessTokenCache = new HashMap<String, AccessToken>();

    @Activate
    protected void activate(ComponentContext componentContext) {
        this.publishClientId = OsgiUtil.toString(componentContext.getProperties().get("clientId"), (String)null);
        this.publishClientSecret = OsgiUtil.toString(componentContext.getProperties().get("clientSecret"), (String)null);
        this.publishEnvironment = OsgiUtil.toString(componentContext.getProperties().get("environment"), (String)null);
        this.minThreadPoolSize = OsgiUtil.toInteger(componentContext.getProperties().get("minThreadPoolSize"), (int)10);
        this.maxThreadPoolSize = OsgiUtil.toInteger(componentContext.getProperties().get("maxThreadPoolSize"), (int)20);
        ModifiableThreadPoolConfig threadPoolConfig = new ModifiableThreadPoolConfig();
        if (threadPoolConfig.getMinPoolSize() < this.minThreadPoolSize) {
            threadPoolConfig.setMinPoolSize(this.minThreadPoolSize);
        }
        if (threadPoolConfig.getMaxPoolSize() < this.maxThreadPoolSize) {
            threadPoolConfig.setMaxPoolSize(this.maxThreadPoolSize);
        }
        threadPoolConfig.setPriority(ThreadPoolConfig.ThreadPriority.NORM);
        this.threadPool = this.threadPoolManager.create((ThreadPoolConfig)threadPoolConfig);
        if (this.threadPool == null) {
            throw new IllegalStateException("Could not get a ThreadPool");
        }
        Properties props = new Properties();
        props.setProperty("http.retries.producer.entity", "5");
        props.setProperty("http.timeout.producer.entity", "30000");
        props.setProperty("http.max_connections_per_host.producer.entity", "5");
        props.setProperty("http.max_connections.producer.entity", "10");
        props.setProperty("http.retries.producer.content", "5");
        props.setProperty("http.timeout.producer.content", "30000");
        props.setProperty("http.max_connections_per_host.producer.content", "5");
        props.setProperty("http.max_connections.producer.content", "10");
        this.httpProperties = props;
        accessTokenCache = new HashMap<String, AccessToken>();
        this.accessTokenExpiryInMins = OsgiUtil.toInteger(componentContext.getProperties().get("accessTokenExpiryInMinutes"), (int)30);
    }

    @Deactivate
    protected void deactivate(ComponentContext context) {
        if (this.threadPool != null) {
            this.threadPoolManager.release(this.threadPool);
            this.threadPoolManager = null;
            this.threadPool = null;
        }
    }

    public String getClientId() {
        return this.publishClientId;
    }

    public String getClientSecret() {
        return this.publishClientSecret;
    }

    public String getEnvironment() {
        return this.publishEnvironment;
    }

    public ThreadPool getThreadPool() {
        return this.threadPool;
    }

    public Properties getHttpProperties() {
        return this.httpProperties;
    }

    public Config getConfig(Configuration configuration) throws DPSException {
        if (configuration == null) {
            throw new DPSException("DPS configuration not set");
        }
        InheritanceValueMap cmap = configuration.getProperties();
        String dpsDeviceId = (String)cmap.get("dpsDeviceId", String.class);
        String dpsDeviceToken = (String)cmap.get("dpsDeviceToken", String.class);
        return this.getConfig(dpsDeviceId, dpsDeviceToken);
    }

    public Config getConfig(String dpsDeviceId, String dpsDeviceToken) throws DPSException {
        try {
            String dpsClientSecret = this.getClientSecret();
            String dpsClientId = this.getClientId();
            String dpsEnvironment = this.getEnvironment();
            Config.Environment env = Config.Environment.production;
            if (dpsEnvironment != null && !dpsEnvironment.isEmpty()) {
                try {
                    env = Config.Environment.valueOf((String)dpsEnvironment);
                }
                catch (Exception ignored) {
                    throw new DPSException("System has not been configured correctly. The environment '" + dpsEnvironment + "' is not valid");
                }
            }
            if (this.cryptoSupportService != null && this.cryptoSupportService.isProtected(dpsDeviceId)) {
                dpsDeviceId = this.cryptoSupportService.unprotect(dpsDeviceId);
            }
            if (this.cryptoSupportService != null && this.cryptoSupportService.isProtected(dpsDeviceToken)) {
                dpsDeviceToken = this.cryptoSupportService.unprotect(dpsDeviceToken);
            }
            if (dpsClientSecret != null && this.cryptoSupportService != null && this.cryptoSupportService.isProtected(dpsClientSecret)) {
                dpsClientSecret = this.cryptoSupportService.unprotect(dpsClientSecret);
            }
            if (dpsClientId != null && this.cryptoSupportService != null && this.cryptoSupportService.isProtected(dpsClientId)) {
                dpsClientId = this.cryptoSupportService.unprotect(dpsClientId);
            }
            Properties accessProperties = new Properties();
            if (dpsClientId == null || dpsClientId.trim().isEmpty() || dpsClientSecret == null || dpsClientSecret.trim().isEmpty()) {
                throw new DPSException("System has not been configured. Experience Manager Mobile ClientID and ClientSecret have not been configured.");
            }
            if (dpsDeviceId == null || dpsDeviceId.trim().isEmpty()) {
                throw new DPSException("App has not been configured. Device id not found in associated Experience Manager Mobile cloud service configuration.");
            }
            if (dpsDeviceToken == null || dpsDeviceToken.trim().isEmpty()) {
                throw new DPSException("App has not been configured. Device token not found in associated Experience Manager Mobile cloud service configuration.");
            }
            accessProperties.setProperty("apiKey", dpsClientId);
            accessProperties.setProperty("clientSecret", dpsClientSecret);
            accessProperties.setProperty("deviceId", dpsDeviceId);
            accessProperties.setProperty("deviceToken", dpsDeviceToken);
            Config config = new Config(env, "AEM.6.2-CQ327235", accessProperties);
            config = config.withHttpClientBuilder(this.httpClientBuilderFactory.newBuilder());
            return config;
        }
        catch (CryptoException ex) {
            throw new DPSException("Failed to build DPS config.", (Throwable)ex);
        }
    }

    public AccessToken getAccessToken(Config config) throws DPSException {
        long timer = System.currentTimeMillis();
        AccessToken accessToken = null;
        if (this.accessTokenExpiryInMins == 0) {
            accessToken = this.createAccessToken(config);
        } else {
            String cacheKey = config.getDeviceId() + config.getDevicetoken();
            if (accessTokenCache.containsKey(cacheKey)) {
                accessToken = accessTokenCache.get(cacheKey);
                LoggerFactory.getLogger((String)("PERF." + this.getClass().getName())).debug("getAccessToken retrieved " + (System.currentTimeMillis() - timer) + "ms");
            } else {
                accessToken = this.createAccessToken(config);
                accessTokenCache.put(cacheKey, accessToken);
                Timer scheduledTimer = new Timer();
                scheduledTimer.schedule((TimerTask)new ClearCache(this, cacheKey), this.accessTokenExpiryInMins * 1000 * 60);
                LoggerFactory.getLogger((String)("PERF." + this.getClass().getName())).debug("getAccessToken created " + (System.currentTimeMillis() - timer) + "ms");
            }
        }
        return accessToken;
    }

    private AccessToken createAccessToken(Config config) throws DPSException {
        AccessToken accessToken = null;
        User user = ImsDao.authenticate((Config)config);
        if (user == null) {
            throw new DPSException("Failed to authenticate. Check refresh token.");
        }
        accessToken = ImsDao.getAccessToken((User)user);
        if (accessToken == null) {
            throw new DPSException("Could not extract DPS token information.");
        }
        return accessToken;
    }

    public static void clearCache(String cacheKey) {
        accessTokenCache.remove(cacheKey);
        LOGGER.debug("Clearing accessToken");
    }

    protected void bindThreadPoolManager(ThreadPoolManager threadPoolManager) {
        this.threadPoolManager = threadPoolManager;
    }

    protected void unbindThreadPoolManager(ThreadPoolManager threadPoolManager) {
        if (this.threadPoolManager == threadPoolManager) {
            this.threadPoolManager = null;
        }
    }

    protected void bindCryptoSupportService(CryptoSupport cryptoSupport) {
        this.cryptoSupportService = cryptoSupport;
    }

    protected void unbindCryptoSupportService(CryptoSupport cryptoSupport) {
        if (this.cryptoSupportService == cryptoSupport) {
            this.cryptoSupportService = null;
        }
    }

    protected void bindHttpClientBuilderFactory(HttpClientBuilderFactory httpClientBuilderFactory) {
        this.httpClientBuilderFactory = httpClientBuilderFactory;
    }

    protected void unbindHttpClientBuilderFactory(HttpClientBuilderFactory httpClientBuilderFactory) {
        if (this.httpClientBuilderFactory == httpClientBuilderFactory) {
            this.httpClientBuilderFactory = null;
        }
    }

    class ClearCache
    extends TimerTask {
        private String cacheKey;
        final /* synthetic */ AdobeDPSClient this$0;

        public ClearCache(AdobeDPSClient adobeDPSClient, String cacheKey) {
            this.this$0 = adobeDPSClient;
            this.cacheKey = cacheKey;
        }

        private ClearCache(AdobeDPSClient adobeDPSClient) {
            this.this$0 = adobeDPSClient;
        }

        @Override
        public void run() {
            AdobeDPSClient.clearCache(this.cacheKey);
        }
    }

}