MobileAppsCloudServiceOperation.java 17.7 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  com.adobe.granite.ui.components.HtmlResponse
 *  com.adobe.granite.xss.XSSAPI
 *  com.day.cq.commons.jcr.JcrUtil
 *  com.day.cq.i18n.I18n
 *  javax.jcr.RepositoryException
 *  javax.jcr.Session
 *  javax.servlet.http.HttpServletRequest
 *  org.apache.commons.lang3.StringUtils
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Properties
 *  org.apache.felix.scr.annotations.Property
 *  org.apache.felix.scr.annotations.Reference
 *  org.apache.felix.scr.annotations.ReferenceCardinality
 *  org.apache.felix.scr.annotations.ReferencePolicy
 *  org.apache.felix.scr.annotations.Service
 *  org.apache.jackrabbit.util.Text
 *  org.apache.sling.api.SlingHttpServletRequest
 *  org.apache.sling.api.resource.Resource
 *  org.apache.sling.api.resource.ResourceResolver
 *  org.apache.sling.api.resource.ResourceUtil
 *  org.apache.sling.commons.osgi.OsgiUtil
 *  org.apache.sling.servlets.post.Modification
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.adobe.cq.mobile.platform.impl.operations;

import com.adobe.cq.mobile.mobilecloudservices.impl.MobileClientCloudServiceHandler;
import com.adobe.cq.mobile.mobileservices.impl.util.AMSUtils;
import com.adobe.cq.mobile.platform.MobileResource;
import com.adobe.cq.mobile.platform.MobileResourceType;
import com.adobe.cq.mobile.platform.impl.MobileAppException;
import com.adobe.cq.mobile.platform.impl.operations.MobileAbstractOperation;
import com.adobe.cq.mobile.platform.impl.operations.MobileOperationException;
import com.adobe.granite.ui.components.HtmlResponse;
import com.adobe.granite.xss.XSSAPI;
import com.day.cq.commons.jcr.JcrUtil;
import com.day.cq.i18n.I18n;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.Service;
import org.apache.jackrabbit.util.Text;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.commons.osgi.OsgiUtil;
import org.apache.sling.servlets.post.Modification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(metatype=0, label="AEM Mobile Cloud Service Updater")
@Service
@Properties(value={@Property(name="sling.post.operation", value={"mobileapps:cloudService"})})
public class MobileAppsCloudServiceOperation
extends MobileAbstractOperation {
    public static final String OPERATION_NAME = "cloudService";
    private final String PROP_CREATE_CONFIG = "cloudConfigCreate";
    private final String PROP_CLOUD_CONFIG_FIELD = "cloudConfigField";
    private static final String PROP_CLOUD_SERVICE_NAME = "cloudServiceName";
    private static final String PROP_CLOUD_CONFIG = "cloudConfig";
    private static final String PROP_ACTION = "configAction";
    private static final String PROP_ACTION_UNLINK = "unlink";
    private static final String PROP_ACTION_CREATESET = "set";
    private final String PROP_TARGET_PATHS = "targetPaths";
    private final String PROP_CLOUD_CONFIG_SUFFIX = "cloudConfigSuffix";
    private static final String CQ_APPS_ADMIN_CLOUDSERVICE_EDIT = "cq-apps-admin-addcloudservice";
    @Reference(name="mobileClientCloudServiceHandler", referenceInterface=MobileClientCloudServiceHandler.class, bind="bindProvisioningHandler", unbind="unbindProvisioningHandler", cardinality=ReferenceCardinality.OPTIONAL_MULTIPLE, policy=ReferencePolicy.DYNAMIC)
    private final Map<String, MobileClientCloudServiceHandler> mobileCloudServiceHandlers = new HashMap<String, MobileClientCloudServiceHandler>();
    private final Logger logger;

    public MobileAppsCloudServiceOperation() {
        this.logger = LoggerFactory.getLogger(this.getClass());
    }

    @Override
    protected void perform(SlingHttpServletRequest request, HtmlResponse response, List<Modification> changes) {
        I18n i18n = new I18n((HttpServletRequest)request);
        String errorTitle = i18n.get("Error");
        Resource resource = request.getResource();
        String action = request.getParameter("configAction");
        try {
            this.validate(request, resource, action);
        }
        catch (MobileOperationException mobOpEx) {
            this.generateError(response, mobOpEx.getMessage(), errorTitle);
            return;
        }
        if (StringUtils.isBlank((CharSequence)action) || action.equals("set")) {
            this.setCloudConfiguration(request, response, changes);
        } else if (action.equals("unlink")) {
            this.unlinkCloudConfiguration(request, response);
        } else {
            this.generateError(response, i18n.get("Unknown action for a Cloud Service Operation: {0}", "unknown action requested", new Object[]{action}), errorTitle);
            return;
        }
    }

    private void setCloudConfiguration(SlingHttpServletRequest request, HtmlResponse response, List<Modification> changes) {
        I18n i18n = new I18n((HttpServletRequest)request);
        ResourceResolver resolver = request.getResourceResolver();
        Session session = (Session)resolver.adaptTo(Session.class);
        String errorTitle = i18n.get("Error");
        String cloudServiceName = request.getParameter("cloudServiceName");
        MobileClientCloudServiceHandler handler = this.getMobileClientCloudServiceHandler(cloudServiceName);
        boolean createConfig = request.getParameter("cloudConfigCreate").equalsIgnoreCase("true");
        String cloudConfigPath = request.getParameter("cloudConfig");
        String propertyName = request.getParameter("cloudConfigField");
        String cloudConfigSuffix = request.getParameter("cloudConfigSuffix");
        if (StringUtils.isBlank((CharSequence)cloudConfigSuffix)) {
            cloudConfigSuffix = "";
        } else if (!(cloudConfigSuffix = JcrUtil.createValidName((String)cloudConfigSuffix)).startsWith("/")) {
            cloudConfigSuffix = "/" + cloudConfigSuffix;
        }
        String path = null;
        try {
            if (createConfig) {
                Resource newResource = this.createCloudConfigAndChildren(request);
                Resource newConfiguration = AMSUtils.getCloudServiceConfiguration(newResource);
                cloudConfigPath = newConfiguration.getPath();
                if (StringUtils.isBlank((CharSequence)cloudConfigSuffix) && !newResource.getPath().equals(cloudConfigPath)) {
                    cloudConfigSuffix = newResource.getPath().substring(cloudConfigPath.length());
                }
                path = cloudConfigPath + cloudConfigSuffix;
            } else {
                path = cloudConfigPath;
            }
        }
        catch (MobileOperationException mobOpEx) {
            this.generateError(response, mobOpEx.getMessage(), errorTitle);
            return;
        }
        try {
            ArrayList<String> failedPaths = this.setCloudConfigOnApp(request, propertyName, path, resolver);
            Resource resourceToUpdate = resolver.getResource(path);
            Map map = request.getParameterMap();
            handler.updateConfiguration(resourceToUpdate, map);
            if (session.hasPendingChanges()) {
                session.save();
            }
            if (!failedPaths.isEmpty()) {
                String failureList = StringUtils.join(failedPaths, (String)", ");
                String message = i18n.get("Property failed to be set on the following paths: {0}", "comma delmited list of failed paths", new Object[]{failureList});
                this.generateError(response, message, errorTitle);
                return;
            }
        }
        catch (RepositoryException repEx) {
            String message = repEx.getMessage();
            this.generateError(response, message, errorTitle);
            return;
        }
        String redirectTo = this.getRedirect(request);
        if (StringUtils.isBlank((CharSequence)redirectTo)) {
            redirectTo = "/aem/apps.html/content/mobileapps";
        } else if (redirectTo.endsWith(".html")) {
            redirectTo = redirectTo + Text.escapePath((String)request.getResource().getPath());
        }
        String editPath = createConfig ? path : Text.getRelativeParent((String)path, (int)1);
        response.addLink("cq-apps-admin-addcloudservice", this.xssAPI.getValidHref(editPath + ".html"), i18n.get("Edit Cloud Configuration"));
        String title = i18n.get("Cloud Config Set");
        String message = i18n.get("Your App has been updated.");
        this.generateResponse(response, 201, message, title, redirectTo, i18n.get("Done"));
    }

    private void unlinkCloudConfiguration(SlingHttpServletRequest request, HtmlResponse response) {
        Resource resource = request.getResource();
        String propertyName = request.getParameter("cloudConfigField");
        String cloudServiceName = request.getParameter("cloudServiceName");
        MobileClientCloudServiceHandler handler = this.getMobileClientCloudServiceHandler(cloudServiceName);
        handler.unlinkCloudConfigProperty(resource, propertyName);
    }

    private void validate(SlingHttpServletRequest request, Resource resource, String action) throws MobileOperationException {
        I18n i18n = new I18n((HttpServletRequest)request);
        ResourceResolver resolver = request.getResourceResolver();
        MobileResource mobileResource = (MobileResource)resource.adaptTo(MobileResource.class);
        if (!(mobileResource.isA(MobileResourceType.INSTANCE.getType()) || mobileResource.isA(MobileResourceType.GROUP.getType()) || mobileResource.isA(MobileResourceType.CONTENT.getType()))) {
            String message = i18n.get("Unable to resolve mobile application {0}", "Path to Resource", new Object[]{resource.getPath()});
            throw new MobileOperationException(message);
        }
        String propertyName = request.getParameter("cloudConfigField");
        if (StringUtils.isBlank((CharSequence)propertyName)) {
            String message = i18n.get("Missing property for configuration property name: {0}", "Field Name", new Object[]{"cloudConfigField"});
            throw new MobileOperationException(message);
        }
        String cloudServiceName = request.getParameter("cloudServiceName");
        if (StringUtils.isBlank((CharSequence)cloudServiceName)) {
            String message = i18n.get("Missing property Cloud Service name.  It is mandatory when creating a new Cloud Service");
            throw new MobileOperationException(message);
        }
        if (StringUtils.isBlank((CharSequence)action) || action.equals("set")) {
            String createConfigString = request.getParameter("cloudConfigCreate");
            if (StringUtils.isBlank((CharSequence)createConfigString)) {
                String message = i18n.get("Missing property to indicate creation of service: {0}", "Field Name", new Object[]{"cloudConfigCreate"});
                throw new MobileOperationException(message);
            }
            boolean createConfig = createConfigString.equalsIgnoreCase("true");
            if (createConfig) {
                MobileClientCloudServiceHandler handler = this.getMobileClientCloudServiceHandler(cloudServiceName);
                if (handler == null) {
                    String message = i18n.get("Invalid Cloud Service specified. It does not have a provisioned handler.");
                    throw new MobileOperationException(message);
                }
                Resource cloudService = resolver.resolve(handler.getServicePath());
                if (ResourceUtil.isNonExistingResource((Resource)cloudService)) {
                    String message = i18n.get("Invalid Cloud Service specified. Path '{0}' does not exist.", "Path to service", new Object[]{cloudService});
                    throw new MobileOperationException(message);
                }
            } else {
                String existingCloudConfig = request.getParameter("cloudConfig");
                if (StringUtils.isBlank((CharSequence)existingCloudConfig)) {
                    String message = i18n.get("Missing property indicating the Cloud Configuration to use.");
                    throw new MobileOperationException(message);
                }
                Resource existingConfigRes = resolver.resolve(existingCloudConfig);
                if (ResourceUtil.isNonExistingResource((Resource)existingConfigRes)) {
                    String message = i18n.get("Cloud Configuration specified does not exist: {0}", "Path to missing config", new Object[]{existingCloudConfig});
                    throw new MobileOperationException(message);
                }
            }
        }
    }

    private ArrayList<String> setCloudConfigOnApp(SlingHttpServletRequest request, String propertyName, String configPath, ResourceResolver resolver) {
        String[] targetPaths = this.getTargetPaths(request);
        String cloudServiceName = request.getParameter("cloudServiceName");
        MobileClientCloudServiceHandler handler = this.getMobileClientCloudServiceHandler(cloudServiceName);
        ArrayList<String> failedPaths = new ArrayList<String>();
        for (String nextTarget : targetPaths) {
            if (!StringUtils.isNotBlank((CharSequence)nextTarget)) continue;
            Resource nextTargetRes = resolver.resolve(nextTarget);
            if (ResourceUtil.isNonExistingResource((Resource)nextTargetRes)) {
                failedPaths.add(nextTarget);
                this.logger.error("Could not apply cloud configuration '" + configPath + "' to '" + nextTarget + "'");
                continue;
            }
            try {
                handler.saveCloudConfigProperty(nextTargetRes, propertyName, configPath);
                continue;
            }
            catch (MobileAppException mobileAppEx) {
                failedPaths.add(nextTarget);
                this.logger.error("Error saving cloud config: " + mobileAppEx.getLocalizedMessage());
                this.logger.error("Could not apply cloud configuration '" + configPath + "' to '" + nextTarget + "'");
            }
        }
        return failedPaths;
    }

    private Resource createCloudConfigAndChildren(SlingHttpServletRequest request) throws MobileOperationException {
        Resource newConfig;
        I18n i18n = new I18n((HttpServletRequest)request);
        ResourceResolver resolver = request.getResourceResolver();
        String cloudServiceName = request.getParameter("cloudServiceName");
        try {
            MobileClientCloudServiceHandler handler = this.getMobileClientCloudServiceHandler(cloudServiceName);
            Map map = request.getParameterMap();
            newConfig = handler.createConfiguration(map, i18n, resolver);
        }
        catch (MobileAppException mobileAppEx) {
            throw new MobileOperationException(mobileAppEx.getMessage());
        }
        return newConfig;
    }

    private String[] getTargetPaths(SlingHttpServletRequest request) {
        int length;
        String[] propertyTargetPaths = request.getParameterValues("targetPaths");
        int n = length = propertyTargetPaths == null ? 0 : propertyTargetPaths.length;
        if (propertyTargetPaths != null) {
            for (String nextPath : propertyTargetPaths) {
                if (!StringUtils.isBlank((CharSequence)nextPath)) continue;
                --length;
            }
        }
        String[] targetPaths = new String[length + 1];
        if (length > 0) {
            for (int i = 0; i < propertyTargetPaths.length; ++i) {
                if (!StringUtils.isNotBlank((CharSequence)propertyTargetPaths[i])) continue;
                targetPaths[i] = propertyTargetPaths[i];
            }
        }
        targetPaths[length] = request.getResource().getPath();
        return targetPaths;
    }

    private MobileClientCloudServiceHandler getMobileClientCloudServiceHandler(String cloudServiceName) {
        return this.mobileCloudServiceHandlers.get(cloudServiceName);
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    protected void bindProvisioningHandler(MobileClientCloudServiceHandler handler, Map<String, Object> properties) {
        String serviceId = OsgiUtil.toString((Object)properties.get("cloudservice.id"), (String)null);
        if (serviceId == null) {
            this.logger.warn("Unnamed MobileClientCloudServiceHandler. Dropping it.");
            return;
        }
        Map<String, MobileClientCloudServiceHandler> map = this.mobileCloudServiceHandlers;
        synchronized (map) {
            this.mobileCloudServiceHandlers.put(serviceId, handler);
        }
        this.logger.info("bound new MobileClientCloudServiceHandler: {}, id={}", (Object)handler.getClass().getName(), (Object)serviceId);
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    protected void unbindProvisioningHandler(MobileClientCloudServiceHandler handler, Map<String, Object> properties) {
        String serviceId = OsgiUtil.toString((Object)properties.get("cloudservice.id"), (String)null);
        Map<String, MobileClientCloudServiceHandler> map = this.mobileCloudServiceHandlers;
        synchronized (map) {
            this.mobileCloudServiceHandlers.remove(serviceId);
        }
        this.logger.info("unbound MobileClientCloudServiceHandler: {}, id={}", (Object)handler.getClass().getName(), (Object)serviceId);
    }
}