com.adobe.people.jedelson.aemslack.impl.Notifier.java Source code

Java tutorial

Introduction

Here is the source code for com.adobe.people.jedelson.aemslack.impl.Notifier.java

Source

/*
 * #%L
 * Copyright (C) 2015 Adobe
 * %%
 * Licensed 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.
 * #L%
 */
package com.adobe.people.jedelson.aemslack.impl;

import java.io.IOException;
import java.util.Collections;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.ConfigurationPolicy;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.PropertyUnbounded;
import org.apache.felix.scr.annotations.Reference;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.adobe.acs.commons.wcm.AuthorUIHelper;
import com.adobe.granite.comments.Comment;
import com.adobe.granite.comments.CommentCollection;
import com.adobe.granite.comments.CommentingEvent;
import com.adobe.granite.taskmanagement.Task;
import com.adobe.granite.taskmanagement.TaskEvent;
import com.adobe.granite.taskmanagement.TaskEventType;
import com.adobe.granite.taskmanagement.TaskManager;
import com.day.cq.commons.Externalizer;

@Component(policy = ConfigurationPolicy.REQUIRE, metatype = true, label = "AEM Slack Integration - Notifier", description = "Event handler for passing AEM notifications to Slack")
public class Notifier {

    public class TaskListener implements EventHandler {
        @Override
        public void handleEvent(Event event) {
            String taskId = PropertiesUtil.toString(event.getProperty(TaskEvent.TASK_ID), null);
            String taskType = PropertiesUtil.toString(event.getProperty(TaskEvent.TASK_EVENT_TYPE_STRING), null);
            if (taskId != null && TaskEventType.TASK_CREATED.name().equals(taskType)) {
                queue.submit(new PostTask(taskId));
            }
        }
    }

    private class CommentListener implements EventHandler {
        @Override
        public void handleEvent(Event event) {
            CommentingEvent ce = CommentingEvent.fromEvent(event);
            if (ce != null) {
                String commentPath = ce.getCommentPath();
                queue.submit(new PostComment(commentPath));
            }
        }
    }

    public class PostTask implements Runnable {

        private String taskId;

        public PostTask(String taskId) {
            this.taskId = taskId;
        }

        @SuppressWarnings("deprecation")
        @Override
        public void run() {
            ResourceResolver resolver = null;
            try {
                resolver = rrFactory.getAdministrativeResourceResolver(null);
                TaskManager taskManager = resolver.adaptTo(TaskManager.class);
                Task task = taskManager.getTask(taskId);
                if (task != null) {
                    String assignee = task.getCurrentAssignee();

                    String slackUsername = usernameMappings.get(assignee);
                    if (slackUsername != null) {
                        String text = String.format("New task received. %s Click <%s|here> to view your task list.",
                                task.getDescription(), externalizer.authorLink(resolver, "/notifications.html"));
                        SlackMessage msg = new SlackMessage(text);
                        msg.setChannel("@" + slackUsername);
                        sendMessage(msg);
                    }
                }
            } catch (Exception e) {
                log.error("Unable to submit comment notification to Slack");
            } finally {
                resolver.close();
            }
        }

    }

    private class PostComment implements Runnable {

        private String commentPath;

        public PostComment(String commentPath) {
            this.commentPath = commentPath;
        }

        @SuppressWarnings("deprecation")
        @Override
        public void run() {
            ResourceResolver resolver = null;
            try {
                resolver = rrFactory.getAdministrativeResourceResolver(null);
                Resource commentResource = resolver.getResource(commentPath);
                if (commentResource != null) {
                    Comment comment = commentResource.adaptTo(Comment.class);
                    CommentCollection<?> collection = comment.getCollection();
                    String targetPath = collection.getTarget().getPath();
                    String viewerPath = null;
                    if (targetPath.startsWith("/content/dam")) {
                        viewerPath = authorUIHelper.generateEditAssetLink(
                                collection.getTarget().getParent().getPath(), true, resolver);
                    }

                    if (viewerPath != null) {
                        String author = comment.getAuthorName();
                        String text;
                        String slackUsername = usernameMappings.get(author);
                        if (slackUsername != null) {
                            text = String.format(
                                    "User <@%s> created comment '%s'. Click <%s|here> to view the asset.",
                                    slackUsername, comment.getMessage(), viewerPath);
                        } else {
                            text = String.format("User %s created comment '%s'. Click <%s|here> to view the asset.",
                                    author, comment.getMessage(), viewerPath);
                        }
                        SlackMessage msg = new SlackMessage(text);
                        sendMessage(msg);
                    }
                }
            } catch (Exception e) {
                log.error("Unable to submit comment notification to Slack");
            } finally {
                resolver.close();
            }
        }
    }

    private static final Logger log = LoggerFactory.getLogger(Notifier.class);

    @Property(label = "Slack Incoming WebHook URL", description = "URL provided by Slack as the Incoming WebHook endpoint.")
    private static final String PROP_URL = "url";

    @Property(unbounded = PropertyUnbounded.ARRAY, label = "AEM to Slack Username Mapping", description = "Mapping between AEM and Slack usernames. In the form <aemname>=<slackname>.")
    private static final String PROP_MAPPING = "username.mapping";

    @Reference
    private AuthorUIHelper authorUIHelper;

    private ServiceRegistration commentListenerRegistration;

    private HttpClient httpClient;

    private ExecutorService queue;

    @Reference
    private ResourceResolverFactory rrFactory;

    @Reference
    private Externalizer externalizer;

    private String url;

    private Map<String, String> usernameMappings = Collections.emptyMap();

    private ServiceRegistration taskListenerRegistration;

    @Activate
    private void activate(ComponentContext ctx) {
        queue = Executors.newSingleThreadExecutor();
        httpClient = new HttpClient();

        Dictionary<?, ?> props = ctx.getProperties();
        url = PropertiesUtil.toString(props.get(PROP_URL), null);
        if (url == null) {
            throw new IllegalArgumentException("URL is not defined");
        }
        usernameMappings = PropertiesUtil.toMap(props.get(PROP_MAPPING), new String[0]);

        BundleContext bundleContext = ctx.getBundleContext();
        Hashtable<String, Object> serviceProps = new Hashtable<String, Object>();
        serviceProps.put(EventConstants.EVENT_TOPIC,
                CommentingEvent.EVENT_TOPIC_BASE + "/" + CommentingEvent.Type.COMMENTED.name().toLowerCase());
        commentListenerRegistration = bundleContext.registerService(EventHandler.class.getName(),
                new CommentListener(), serviceProps);

        serviceProps.put(EventConstants.EVENT_TOPIC, TaskEvent.TOPIC);
        taskListenerRegistration = bundleContext.registerService(EventHandler.class.getName(), new TaskListener(),
                serviceProps);

    }

    @Deactivate
    private void deactivate() {
        if (commentListenerRegistration != null) {
            commentListenerRegistration.unregister();
            commentListenerRegistration = null;
        }
        if (taskListenerRegistration != null) {
            taskListenerRegistration.unregister();
            taskListenerRegistration = null;
        }
        queue.shutdownNow();
    }

    private void sendMessage(SlackMessage msg) throws HttpException, IOException {
        PostMethod pm = new PostMethod(url);
        pm.setRequestEntity(new StringRequestEntity(msg.toJsonString(), "application/json", "UTF-8"));
        httpClient.executeMethod(pm);
    }
}