org.wso2.carbon.apimgt.gateway.utils.APIMgtGoogleAnalyticsUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.apimgt.gateway.utils.APIMgtGoogleAnalyticsUtils.java

Source

/*
 * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * 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.
 */

package org.wso2.carbon.apimgt.gateway.utils;

import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.apache.axis2.util.JavaUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.apimgt.impl.APIConstants;
import org.wso2.carbon.apimgt.impl.internal.ServiceReferenceHolder;
import org.wso2.carbon.apimgt.usage.publisher.APIMgtUsagePublisherConstants;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.ganalytics.publisher.GoogleAnalyticsConstants;
import org.wso2.carbon.ganalytics.publisher.GoogleAnalyticsData;
import org.wso2.carbon.ganalytics.publisher.GoogleAnalyticsDataPublisher;
import org.wso2.carbon.registry.core.Registry;
import org.wso2.carbon.registry.core.Resource;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

public class APIMgtGoogleAnalyticsUtils {
    private static final Log log = LogFactory.getLog(APIMgtGoogleAnalyticsUtils.class);
    private static final String ANONYMOUS_USER_ID = "anonymous";
    private static final String GOOGLE_ANALYTICS_TRACKER_VERSION = "1";
    private String configKey = null;
    private GoogleAnalyticsConfig gaConfig = null;

    private class GoogleAnalyticsConfig {
        private boolean enabled;
        private String googleAnalyticsTrackingID;

        public GoogleAnalyticsConfig(OMElement config) {
            googleAnalyticsTrackingID = config.getFirstChildWithName(
                    new QName(APIMgtUsagePublisherConstants.API_GOOGLE_ANALYTICS_TRACKING_ID)).getText();
            String googleAnalyticsEnabledStr = config
                    .getFirstChildWithName(
                            new QName(APIMgtUsagePublisherConstants.API_GOOGLE_ANALYTICS_TRACKING_ENABLED))
                    .getText();
            enabled = googleAnalyticsEnabledStr != null && JavaUtils.isTrueExplicitly(googleAnalyticsEnabledStr);
        }
    }

    /**
     * Initialize the google analytics publisher by reading tenants google analytics
     * configuration from the registry
     *
     * @param tenantDomain Tenant domain of the current tenant
     */
    public void init(String tenantDomain) {
        configKey = APIConstants.GA_CONFIGURATION_LOCATION;

        try {
            PrivilegedCarbonContext.startTenantFlow();
            PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true);

            Registry registry = ServiceReferenceHolder.getInstance().getRegistryService()
                    .getGovernanceSystemRegistry();
            Resource resource = registry.get(configKey);
            InputStream in = resource.getContentStream();
            StAXOMBuilder builder = new StAXOMBuilder(in);
            this.gaConfig = new GoogleAnalyticsConfig(builder.getDocumentElement());
        } catch (RegistryException | XMLStreamException e) {
            // flow should not break. Therefore ignoring the exception
            log.error("Failed to retrieve google analytics configurations for tenant:" + tenantDomain);
        } finally {
            PrivilegedCarbonContext.endTenantFlow();
        }
    }

    /**
     * Initialize the google analytics publisher using provided xml configurations
     *
     * @param config Google Analytics configuration element
     */
    public void init(OMElement config) {
        this.gaConfig = new GoogleAnalyticsConfig(config);
    }

    /**
     * Generates a 32 character length random number for cacheBusterId
     *
     * @return cacheBusterId
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     */
    public static String getCacheBusterId() throws NoSuchAlgorithmException, UnsupportedEncodingException {
        String message = getRandomNumber() + UUID.randomUUID().toString();
        MessageDigest m = MessageDigest.getInstance("MD5");
        m.update(message.getBytes("UTF-8"), 0, message.length());
        byte[] sum = m.digest();
        BigInteger messageAsNumber = new BigInteger(1, sum);
        String md5String = messageAsNumber.toString(16);

        // Pad to make sure id is 32 characters long.
        while (md5String.length() < 32) {
            md5String = "0" + md5String;
        }
        return "0x" + md5String.substring(0, 16);
    }

    /**
     * Generate a random number
     *
     * @return random number
     */
    private static String getRandomNumber() {
        return Integer.toString((int) (Math.random() * 0x7fffffff));
    }

    /**
     * Generate a visitor id for this hit. If there is a visitor id in the
     * messageContext, use that. Otherwise use a random number.
     *
     * @param authHeader Authentication header of the request
     */
    private String getVisitorId(String authHeader) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        String message = authHeader.split(" ")[1];
        if (message == null) {
            message = ANONYMOUS_USER_ID;
        }

        MessageDigest m = MessageDigest.getInstance("MD5");
        m.update(message.getBytes("UTF-8"), 0, message.length());
        byte[] sum = m.digest();
        BigInteger messageAsNumber = new BigInteger(1, sum);
        String md5String = messageAsNumber.toString(16);

        // Pad to make sure id is 32 characters long.
        while (md5String.length() < 32) {
            md5String = "0" + md5String;
        }

        return "0x" + md5String.substring(0, 16);
    }

    /**
     * Publish page tracking data to google analytics
     *
     * @param analyticsData Attributes required to be sent to Google Analytics
     * @param userAgent User-Agent of the client who sent the request
     * @param authHeader authentication header value of the request
     */
    public void publishGATrackingData(GoogleAnalyticsData.DataBuilder analyticsData, String userAgent,
            String authHeader) {
        try {
            if (gaConfig == null || !gaConfig.enabled) {
                return;
            }
            String clientId = getVisitorId(authHeader);
            GoogleAnalyticsData data = analyticsData.setProtocolVersion(GOOGLE_ANALYTICS_TRACKER_VERSION)
                    .setTrackingId(gaConfig.googleAnalyticsTrackingID).setClientId(clientId)
                    .setHitType(GoogleAnalyticsConstants.HIT_TYPE_PAGEVIEW).build();

            String payload = GoogleAnalyticsDataPublisher.buildPayloadString(data);
            GoogleAnalyticsDataPublisher.publishGET(payload, userAgent, false);
        } catch (Exception e) {
            // flow should not break if event publishing failed. Therefore catching generic Exception and ignoring
            log.error("Cannot publish event. " + e.getMessage(), e);
        }
    }
}