org.wso2.carbon.registry.eventing.services.EventingServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.registry.eventing.services.EventingServiceImpl.java

Source

/*
 *  Copyright (c) 2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. 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.wso2.carbon.registry.eventing.services;

import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.apache.axiom.om.util.AXIOMUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.CarbonException;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.registry.common.eventing.RegistryEvent;
import org.wso2.carbon.registry.common.utils.RegistryUtil;
import org.wso2.carbon.registry.core.Resource;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import org.wso2.carbon.registry.core.session.UserRegistry;
import org.wso2.carbon.registry.event.core.exception.EventBrokerException;
import org.wso2.carbon.registry.event.core.subscription.EventDispatcher;
import org.wso2.carbon.registry.event.core.subscription.Subscription;
import org.wso2.carbon.registry.eventing.RegistryEventDispatcher;
import org.wso2.carbon.registry.eventing.RegistryEventingConstants;
import org.wso2.carbon.registry.eventing.events.ChildCreatedEvent;
import org.wso2.carbon.registry.eventing.events.ChildDeletedEvent;
import org.wso2.carbon.registry.eventing.events.CollectionAddedEvent;
import org.wso2.carbon.registry.eventing.events.CollectionCreatedEvent;
import org.wso2.carbon.registry.eventing.events.CollectionDeletedEvent;
import org.wso2.carbon.registry.eventing.events.CollectionUpdatedEvent;
import org.wso2.carbon.registry.eventing.events.DispatchEvent;
import org.wso2.carbon.registry.eventing.events.ResourceAddedEvent;
import org.wso2.carbon.registry.eventing.events.ResourceCreatedEvent;
import org.wso2.carbon.registry.eventing.events.ResourceDeletedEvent;
import org.wso2.carbon.registry.eventing.events.ResourceUpdatedEvent;
import org.wso2.carbon.registry.eventing.internal.EventingDataHolder;
import org.wso2.carbon.utils.CarbonUtils;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class EventingServiceImpl implements EventingService, SubscriptionEmailVerficationService {

    private Map<String, String[]> eventTypeMap;
    private Map<String, List<String>> eventTypeExclusionMap;
    private static boolean initialized = false;
    private static EventDispatcher dispatcher = null;
    private ExecutorService executor = null;
    private String emailIndexStoragePath;
    public static List<String> listOfMediaTypes = new ArrayList<String>();
    private static final String MEDIA_TYPE_MATCHER_FILTER_CLASS = "org.wso2.carbon.registry.core.jdbc.handlers.filters.MediaTypeMatcher";
    private static final Log log = LogFactory.getLog(EventingServiceImpl.class);

    public String verifyEmail(String data) {
        String subscriptionId = null;
        String username = null;
        String remoteURL = null;
        String email = null;

        if (data != null) {
            try {
                OMElement dataElement = AXIOMUtil.stringToOM(data);
                Iterator it = dataElement.getChildElements();
                while (it.hasNext()) {
                    OMElement element = (OMElement) it.next();
                    if ("subscriptionId".equals(element.getLocalName())) {
                        subscriptionId = element.getText();
                    } else if ("username".equals(element.getLocalName())) {
                        username = element.getText();
                    } else if ("remoteURL".equals(element.getLocalName())) {
                        remoteURL = element.getText();
                    } else if ("email".equals(element.getLocalName())) {
                        email = element.getText();
                    }
                }
            } catch (XMLStreamException e) {
                log.error("Unable to verify e-mail address", e);
                return null;
            }
        }

        try {
            if (username != null || remoteURL != null) {
                throw new UnsupportedOperationException("This method is no longer supported");
            } else {
                Subscription subscription = getSubscription(subscriptionId);
                Map<String, String> subscriptionData = subscription.getProperties();
                subscriptionData.remove(RegistryEventingConstants.NOT_VERIFIED);
                subscription.setProperties(subscriptionData);
                // The updated subscription should be directly done at the subMgr to avoid
                // generating another verification request
                subscription.setId(subscriptionId);
                EventingDataHolder.getInstance().getRegistryEventBrokerService().renewSubscription(subscription);
            }
            return email;
        } catch (Exception e) {
            log.error("Unable to verify e-mail address", e);
            return null;
        }
    }

    public EventingServiceImpl() {
        try {
            if (initialized) {
                return;
            } else {
                initialized = true;
            }
            eventTypeMap = new TreeMap<String, String[]>();
            eventTypeExclusionMap = new HashMap<String, List<String>>();
            emailIndexStoragePath = "/repository/components/org.wso2.carbon.email-verification/emailIndex";
            registerBuiltInEventTypes();
            storeHandlerConfig();
        } catch (Exception e) {
            log.error("Error initializing Registry Event Broker");
        }
    }

    public void registerBuiltInEventTypes() {
        registerEventType("update", ResourceUpdatedEvent.EVENT_NAME, CollectionUpdatedEvent.EVENT_NAME);
        registerEventType("delete", ResourceDeletedEvent.EVENT_NAME, CollectionDeletedEvent.EVENT_NAME);
        registerEventType("child.deleted", null, ChildDeletedEvent.EVENT_NAME);
        registerEventType("child.created", null, ChildCreatedEvent.EVENT_NAME);
    }

    private static void storeHandlerConfig() {
        String configPath = CarbonUtils.getRegistryXMLPath();
        Set<String> hashSet = new HashSet<>();
        if (configPath != null) {
            File registryXML = new File(configPath);
            try {
                InputStream configInputStream = new FileInputStream(registryXML);
                StAXOMBuilder builder = new StAXOMBuilder(
                        CarbonUtils.replaceSystemVariablesInXml(configInputStream));
                OMElement configElement = builder.getDocumentElement();
                Iterator<OMElement> handlerConfigs = configElement.getChildrenWithName(new QName("handler"));
                while (handlerConfigs.hasNext()) {
                    OMElement handlerConfigElement = handlerConfigs.next();
                    OMElement filter = handlerConfigElement.getFirstChildWithName(new QName("filter"));
                    String filterClassName = filter.getAttributeValue(new QName("class"));
                    Class filterClass = Class.forName(filterClassName);
                    Class mediaTypeMatcher = Class.forName(MEDIA_TYPE_MATCHER_FILTER_CLASS);
                    if (filterClass.getGenericSuperclass().equals(mediaTypeMatcher)) {
                        Method method = filterClass.getMethod("getMediaType", null);
                        Object returnValue = method.invoke(filterClass.newInstance(), null);
                        listOfMediaTypes.add((String) returnValue);
                    }
                    OMElement property = filter.getFirstChildWithName(new QName("property"));
                    if (property != null) {
                        if (MEDIA_TYPE_MATCHER_FILTER_CLASS.equals(filterClassName)
                                && "mediaType".equals(property.getAttributeValue(new QName("name")))) {
                            listOfMediaTypes.add(property.getText());
                        }
                    }
                }
                hashSet.addAll(listOfMediaTypes);
                listOfMediaTypes.clear();
                listOfMediaTypes.addAll(hashSet);
            } catch (FileNotFoundException e) {
                log.error("registry.xml file not found", e);
            } catch (CarbonException e) {
                log.error("Error in converting registry xml inputstream", e);
            } catch (XMLStreamException e) {
                log.error("Error in registry xml stream", e);
            } catch (NoSuchMethodException e) {
                log.error("No such method to invoke", e);
            } catch (IllegalAccessException e) {
                log.error("Error when creating new instance", e);
            } catch (InstantiationException e) {
                log.error("Error when creating new instance", e);
            } catch (InvocationTargetException e) {
                log.error("Error when invoking the java reflection method", e);
            } catch (ClassNotFoundException e) {
                log.error("Error in Class.forName method", e);
            }
        }

    }

    public void notify(RegistryEvent event) throws Exception {
        notify(event, null);
    }

    public void notify(RegistryEvent event, String endpoint) throws Exception {
        notify(event, endpoint, false);
    }

    private synchronized static void initializeDispatcher() {
        if (dispatcher == null) {
            dispatcher = new RegistryEventDispatcher();
            ((RegistryEventDispatcher) dispatcher).init(EventingDataHolder.getInstance().getConfigurationContext());
        }
    }

    public void notify(RegistryEvent event, String endpoint, boolean doRest) throws Exception {
        if (!initialized) {
            return;
        }
        if (executor == null) {
            setupExecutorService();
        }
        if (dispatcher == null) {
            try {
                initializeDispatcher();
            } catch (IllegalStateException ignored) {
                // We throw an Illegal State Exception only if the dispatcher failed to initialize.
                // There is no point in continuing after that. Such a scenario can happen if the
                // very first event was generated during shutdown and we are unable to create
                // corresponding dispatch queues.
                return;
            }
        }
        executor.submit(new Publisher(new DispatchEvent(event, endpoint, doRest)));
    }

    public void registerEventType(String typeId, String resourceEvent, String collectionEvent) {
        String[] eventNames = new String[2];
        eventNames[0] = resourceEvent;
        eventNames[1] = collectionEvent;
        eventTypeMap.put(typeId, eventNames);
    }

    public List<Subscription> getAllSubscriptions() throws EventBrokerException {
        return EventingDataHolder.getInstance().getRegistryEventBrokerService().getAllSubscriptions(null);
    }

    public List<Subscription> getAllSubscriptions(String userName, String remoteURL) throws EventBrokerException {
        throw new UnsupportedOperationException("This method is no longer supported");
    }

    public Map getEventTypes() {
        return eventTypeMap;
    }

    public String getSubscriptionManagerUrl() {
        return EventingDataHolder.getInstance().getDefaultEventingServiceURL();
    }

    public Subscription getSubscription(String id) {
        try {
            if (id != null && id.contains(";version:")) {
                log.warn(
                        "Versioned resources cannot have subscriptions, instead returns the subscription from the actual resource");
                return EventingDataHolder.getInstance().getRegistryEventBrokerService()
                        .getSubscription(RegistryUtil.getResourcePathFromVersionPath(id));
            } else {
                return EventingDataHolder.getInstance().getRegistryEventBrokerService().getSubscription(id);
            }
        } catch (EventBrokerException e) {
            log.error("Unable to get subscription for given id: " + id, e);
            return null;
        }
    }

    private void requestEmailVerification(Subscription subscription, String userName, String remoteURL) {
        int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
        if (executor == null) {
            setupExecutorService();
        }
        if (!subscription.getProperties().isEmpty()) {
            executor.submit(new EmailVerifier(subscription, userName, remoteURL, tenantId));
        }
    }

    private synchronized void setupExecutorService() {
        if (executor == null) {
            executor = new ThreadPoolExecutor(25, 150, 1000, TimeUnit.NANOSECONDS,
                    new ArrayBlockingQueue<Runnable>(100));
        }
    }

    public String subscribe(Subscription subscription, String userName, String remoteURL) {
        throw new UnsupportedOperationException("This method is no longer supported");
    }

    public String subscribe(Subscription subscription) {
        try {
            boolean emailAvailability = isEmailAlreadyAvailable(subscription);
            if ((subscription.getEventSinkURL().startsWith("mailto:")
                    || subscription.getEventSinkURL().startsWith("digest:")) && !emailAvailability) {
                Map<String, String> subscriptionData = subscription.getProperties();
                subscriptionData.put(RegistryEventingConstants.NOT_VERIFIED, Boolean.toString(true));
                subscription.setProperties(subscriptionData);
            }
            String subscribe = EventingDataHolder.getInstance().getRegistryEventBrokerService()
                    .subscribe(subscription);
            requestEmailVerification(subscription, null, null);
            return subscribe;
        } catch (EventBrokerException e) {
            log.error("Unable to add subscription", e);
            return null;
        }
    }

    private boolean isEmailAlreadyAvailable(Subscription subscription) {
        // if one-time email verification is false or not defined, we do not need to check resource, and we will treat it as the resource was
        // not available.
        if (Boolean.parseBoolean(System.getProperty("onetime.email.verification", Boolean.toString(false)))) {
            String email = subscription.getEventSinkURL().substring(subscription.getEventSinkURL().indexOf(":") + 1,
                    subscription.getEventSinkURL().length());
            try {
                UserRegistry registry = EventingDataHolder.getInstance().getRegistryService()
                        .getConfigSystemRegistry();
                String emailIndexPath = this.emailIndexStoragePath;
                Resource emailIndexResource;
                if (registry.resourceExists(emailIndexPath)) {
                    emailIndexResource = registry.get(emailIndexPath);
                    Collection<Object> values = emailIndexResource.getProperties().values();
                    for (Iterator it = values.iterator(); it.hasNext();) {
                        String value = (((ArrayList) (it.next())).toArray())[0].toString();
                        if (value.equals(email)) {
                            return true;
                        }
                    }
                } else {
                    emailIndexResource = registry.newResource();
                    registry.put(emailIndexPath, emailIndexResource);
                }
            } catch (RegistryException e) {
                log.error("Unable to check email verification resource", e);
            }
        }
        return false;
    }

    public Subscription getSubscription(String id, String userName, String remoteURL) {
        throw new UnsupportedOperationException("This method is no longer supported");
    }

    public boolean unsubscribe(String subscriptionID) {
        try {
            EventingDataHolder.getInstance().getRegistryEventBrokerService().unsubscribe(subscriptionID);
            return true;
        } catch (EventBrokerException e) {
            log.error("Unable to unsubscribe using given id: " + subscriptionID, e);
            return false;
        }
    }

    public boolean unsubscribe(String subscriptionID, String userName, String remoteURL) {
        throw new UnsupportedOperationException("This method is no longer supported");
    }

    public void registerEventTypeExclusion(String typeId, String path) {
        if (path == null) {
            return;
        }
        List<String> list = eventTypeExclusionMap.get(typeId);
        if (list == null) {
            list = new LinkedList<String>();
        }
        list.add(path);
        eventTypeExclusionMap.put(typeId, list);
    }

    public boolean isEventTypeExclusionRegistered(String typeId, String path) {
        List<String> list = eventTypeExclusionMap.get(typeId);
        if (list == null) {
            return false;
        }
        for (String s : list) {
            if (path.matches(s)) {
                return true;
            }
        }
        return false;
    }

    private static class EmailVerifier implements Runnable {

        private Subscription subscription;
        private String userName;
        private String remoteURL;
        private int tenantId;
        private String tenantDomain;

        public EmailVerifier(Subscription subscription, String userName, String remoteURL, int tenantId) {
            this.subscription = subscription;
            this.userName = userName;
            this.remoteURL = remoteURL;
            this.tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
            this.tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
        }

        @SuppressWarnings("unchecked")
        public void run() {
            PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain);
            PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(tenantId, true);
            if (EventingDataHolder.getInstance().getEmailVerificationSubscriber() == null) {
                return;
            }
            String eventUrl = subscription.getEventSinkURL();
            String lowerCasedEventUrl = eventUrl.toLowerCase();

            String email = "";
            if (lowerCasedEventUrl.startsWith("digest:") && lowerCasedEventUrl.contains("mailto")) {
                // extracting the mailto: address form eventsink url like digest://h/mailto:email
                email = eventUrl.substring("digest:".length() + 4);
            } // user verification for emails.
            else if (lowerCasedEventUrl.contains("mailto:")) {
                email = eventUrl;
            }
            // we are not requesting verifications for user and roles.
            else {
                return;
            }
            email = email.substring("mailto:".length());
            Map<String, String> data = new HashMap<String, String>();
            data.put("email", email);
            data.put("subscriptionId", subscription.getId());
            if (userName != null) {
                data.put("username", userName);
            }
            if (remoteURL != null) {
                data.put("remoteURL", remoteURL);
            }
            try {
                EventingDataHolder.getInstance().getEmailVerificationSubscriber().requestUserVerification(data,
                        EventingDataHolder.getInstance().getEmailVerifierConfig());
            } catch (Exception e) {
                log.error("Unable to create e-mail verification request", e);
            }
        }
    }

    private static class Publisher implements Runnable {

        private DispatchEvent event;

        public Publisher(DispatchEvent event) {
            this.event = event;
        }

        public void run() {
            try {
                // Setup the Carbon Context so that downstream logic can make use of it.
                // At present, the Event Component makes use of the information stored on the Carbon
                // Context when publishing events.
                PrivilegedCarbonContext.startTenantFlow();
                try {
                    PrivilegedCarbonContext context = PrivilegedCarbonContext.getThreadLocalCarbonContext();
                    int tenantId = event.getTenantId();
                    if (tenantId != -1) {
                        context.setTenantId(tenantId, true);
                    }
                    RegistryEvent.RegistrySession registrySessionDetails = event.getRegistrySessionDetails();
                    if (registrySessionDetails != null) {
                        String username = registrySessionDetails.getUsername();
                        if (username != null) {
                            context.setUsername(username);
                        }
                    }
                    // For each resource-event also generate a corresponding collection event, so that any hierarchical subscriptions would
                    // work.
                    if (event.getTopic().contains(ResourceUpdatedEvent.EVENT_NAME)) {
                        EventingDataHolder.getInstance().getRegistryEventBrokerService().publish(event,
                                event.getTopic());
                        EventingDataHolder.getInstance().getRegistryEventBrokerService().publish(event,
                                event.getTopic().replace(ResourceUpdatedEvent.EVENT_NAME,
                                        CollectionUpdatedEvent.EVENT_NAME));
                    } else if (event.getTopic().contains(ResourceAddedEvent.EVENT_NAME)) {
                        EventingDataHolder.getInstance().getRegistryEventBrokerService().publish(event,
                                event.getTopic());
                        EventingDataHolder.getInstance().getRegistryEventBrokerService().publish(event,
                                event.getTopic().replace(ResourceAddedEvent.EVENT_NAME,
                                        CollectionAddedEvent.EVENT_NAME));
                    } else if (event.getTopic().contains(ResourceCreatedEvent.EVENT_NAME)) {
                        EventingDataHolder.getInstance().getRegistryEventBrokerService().publish(event,
                                event.getTopic());
                        EventingDataHolder.getInstance().getRegistryEventBrokerService().publish(event,
                                event.getTopic().replace(ResourceCreatedEvent.EVENT_NAME,
                                        CollectionCreatedEvent.EVENT_NAME));
                    } else if (event.getTopic().contains(ResourceDeletedEvent.EVENT_NAME)) {
                        EventingDataHolder.getInstance().getRegistryEventBrokerService().publish(event,
                                event.getTopic());
                        EventingDataHolder.getInstance().getRegistryEventBrokerService().publish(event,
                                event.getTopic().replace(ResourceDeletedEvent.EVENT_NAME,
                                        CollectionDeletedEvent.EVENT_NAME));
                    } else {
                        EventingDataHolder.getInstance().getRegistryEventBrokerService().publish(event,
                                event.getTopic());
                    }
                } finally {
                    PrivilegedCarbonContext.endTenantFlow();
                }
            } catch (EventBrokerException e) {
                log.error("Unable to send notification", e);
            }
        }
    }

}