com.magnet.mmx.server.plugin.mmxmgmt.handler.DeviceHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.magnet.mmx.server.plugin.mmxmgmt.handler.DeviceHandler.java

Source

/*   Copyright (c) 2015 Magnet Systems, Inc.
 *
 *  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 com.magnet.mmx.server.plugin.mmxmgmt.handler;

import com.magnet.mmx.protocol.*;
import com.magnet.mmx.server.common.data.AppEntity;
import com.magnet.mmx.server.plugin.mmxmgmt.db.*;
import com.magnet.mmx.server.plugin.mmxmgmt.event.MMXMaxDevicesPerAppLimitReachedEvent;
import com.magnet.mmx.server.plugin.mmxmgmt.util.*;
import com.magnet.mmx.util.GsonData;
import com.magnet.mmx.util.Utils;
import org.dom4j.Element;
import org.jivesoftware.openfire.IQHandlerInfo;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.handler.IQHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * Handler that handles the requests for managing devices
 */
public class DeviceHandler extends IQHandler {
    private static Logger LOGGER = LoggerFactory.getLogger(DeviceHandler.class);
    private static final String COMMAND_KEY = com.magnet.mmx.protocol.Constants.MMX_ATTR_COMMAND;

    /**
     * Constructor that takes the module name
     * @param moduleName
     */
    public DeviceHandler(String moduleName) {
        super(moduleName);
    }

    @Override
    public IQ handleIQ(IQ iq) throws UnauthorizedException {
        LOGGER.info("handleIQ is called");
        AppDAO dao = new AppDAOImpl(new OpenFireDBConnectionProvider());
        JID fromJID = iq.getFrom();
        String appId = JIDUtil.getAppId(fromJID);

        AppEntity appEntity = dao.getAppForAppKey(appId);
        if (appEntity == null) {
            return IQUtils.createErrorIQ(iq, DeviceOperationStatusCode.INVALID_APP_ID.getMessage(),
                    DeviceOperationStatusCode.INVALID_APP_ID.getCode());
        }
        /*
          retrieve the command that we are processing
         */
        Element element = iq.getChildElement();
        String payload = element.getText();
        String command = element.attributeValue(COMMAND_KEY);
        if (command == null || command.isEmpty() || command.trim().isEmpty()) {
            return IQUtils.createErrorIQ(iq, DeviceOperationStatusCode.INVALID_COMMAND_VALUE.getMessage(),
                    DeviceOperationStatusCode.INVALID_COMMAND_VALUE.getCode());
        }

        Constants.DeviceCommand deviceCommand = null;
        try {
            deviceCommand = Constants.DeviceCommand.valueOf(command.toUpperCase());
        } catch (IllegalArgumentException e) {
            LOGGER.info("Invalid device command string:" + command, e);
        }
        if (deviceCommand == null) {
            return IQUtils.createErrorIQ(iq, DeviceOperationStatusCode.INVALID_COMMAND_VALUE.getMessage(),
                    DeviceOperationStatusCode.INVALID_COMMAND_VALUE.getCode());
        }

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Processing command:" + deviceCommand.toString());
        }

        switch (deviceCommand) {
        case REGISTER:
            return processRegistration(iq, fromJID, appEntity, payload);
        case UNREGISTER:
            return processDeRegistration(iq, fromJID, appEntity, payload);
        case QUERY:
            return processQuery(iq, fromJID, appEntity, payload);
        case GETTAGS:
            return processGetTags(iq, fromJID, appEntity, payload);
        case SETTAGS:
            return processSetTags(iq, fromJID, appEntity, payload);
        case ADDTAGS:
            return processAddTags(iq, fromJID, appEntity, payload);
        case REMOVETAGS:
            return processRemoveTags(iq, fromJID, appEntity, payload);
        }
        return null;
    }

    @Override
    public IQHandlerInfo getInfo() {
        return new IQHandlerInfo(Constants.MMX, Constants.MMX_NS_DEV);
    }

    // Process device registration
    private IQ processRegistration(IQ input, JID fromJID, AppEntity appEntity, String payload) {
        DeviceDAO deviceDAO = new DeviceDAOImpl(new OpenFireDBConnectionProvider());
        String appId = appEntity.getAppId();
        String userId = JIDUtil.getUserId(fromJID);

        DevReg deviceRequest = DevReg.fromJson(payload);

        IQ error = validateRequest(input, deviceRequest);
        if (error != null) {
            //validation failed
            return error;
        }

        DeviceEntity deviceEntity = deviceDAO.getDevice(deviceRequest.getDevId(),
                Helper.enumerateOSType(deviceRequest.getOsType()), appId);
        if (deviceEntity != null) {
            //this is an update
            String oldToken = deviceEntity.getClientToken();
            String newToken = deviceRequest.getPushToken();
            boolean changed = false;
            if (oldToken == null && newToken == null) {
                changed = false;
            } else if (oldToken == null && newToken != null) {
                changed = true;
            } else if (oldToken != null && newToken == null) {
                changed = true;
            } else if (!oldToken.equals(newToken)) {
                changed = true;
            }
            int rowCount = deviceDAO.updateDevice(deviceRequest.getDevId(),
                    Helper.enumerateOSType(deviceRequest.getOsType()), appId, deviceRequest, userId,
                    DeviceStatus.ACTIVE);

            if (changed) {
                PushStatus status = null;
                if (newToken != null) {
                    status = PushStatus.VALID;
                }
                LOGGER.trace("processRegistration : updateDevice resultCount={}, deviceId={}", rowCount,
                        deviceRequest.getDevId());
                deviceDAO.updatePushStatus(deviceRequest.getDevId(),
                        Helper.enumerateOSType(deviceRequest.getOsType()), appId, status);
            }
        } else {
            int id = deviceDAO.addDevice(userId, appId, deviceRequest);
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug(String.format("Device created with id:%d using request:%s for app id: %s", id,
                        deviceRequest.toString(), appId));
            }
        }

        MMXStatus devResp = new MMXStatus().setCode(DeviceOperationStatusCode.DEVICE_REGISTERED.getCode())
                .setMessage(DeviceOperationStatusCode.DEVICE_REGISTERED.getMessage());
        return IQUtils.createResultIQ(input, devResp.toJson());
    }

    private IQ processDeRegistration(IQ input, JID fromJID, AppEntity appEntity, String payload) {
        DeviceDAO deviceDAO = new DeviceDAOImpl(new OpenFireDBConnectionProvider());
        int appId = appEntity.getId();
        DevId rqt = DevId.fromJson(payload);

        IQ error = validateRequest(input, rqt);
        if (error != null) {
            //validation failed
            return error;
        }
        int id = deviceDAO.deactivateDevice(rqt.getDevId());
        MMXStatus devResp = new MMXStatus().setCode(DeviceOperationStatusCode.DEVICE_UNREGISTERED.getCode());
        return IQUtils.createResultIQ(input, devResp.toJson());
    }

    private DeviceInfo convert(DeviceEntity entity) {
        DeviceInfo devInfo = new DeviceInfo().setCarrierInfo(entity.getCarrierInfo()).setDevId(entity.getDeviceId())
                .setDisplayName(entity.getName()).setModelInfo(null)
                .setOsType(entity.getOsType() == null ? null : entity.getOsType().toString())
                .setOsVersion(entity.getOsVersion()).setPhoneNumber(entity.getPhoneNumber()).setExtras(null)
                .setPushType(entity.getTokenType() == null ? null : entity.getTokenType().toString())
                .setPushToken(entity.getClientToken()).setTags(null);
        return devInfo;
    }

    // Process the query for device ID's owned by a user using user ID.
    private IQ processQuery(IQ input, JID fromJID, AppEntity appEntity, String payload) {
        DeviceDAO deviceDAO = new DeviceDAOImpl(new OpenFireDBConnectionProvider());
        String appId = appEntity.getAppId();
        String userId = GsonData.getGson().fromJson(payload, String.class);

        if (userId == null) {
            userId = JIDUtil.getUserId(fromJID);
        }
        IQ error = validateRequest(input, userId);
        if (error != null) {
            return error;
        }
        List<DeviceEntity> list = deviceDAO.getDevices(appId, userId, DeviceStatus.ACTIVE);
        DevList devList = new DevList(list.size());
        for (DeviceEntity entity : list) {
            devList.add(convert(entity));
        }
        return IQUtils.createResultIQ(input, GsonData.getGson().toJson(devList));
    }

    private IQ validateRequest(IQ input, DevReg deviceRequest) {
        String apiKey = deviceRequest.getApiKey();
        AppDAO appDAO = new AppDAOImpl(new OpenFireDBConnectionProvider());
        AppEntity app = appDAO.getAppUsingAPIKey(apiKey);

        String ownerJid = JIDUtil.getUserId(input.getFrom());
        if (app == null) {
            return IQUtils.createErrorIQ(input, DeviceOperationStatusCode.INVALID_API_KEY.getMessage(),
                    DeviceOperationStatusCode.INVALID_API_KEY.getCode());
        }
        String deviceId = deviceRequest.getDevId();
        if (deviceId == null || deviceId.isEmpty()) {
            return IQUtils.createErrorIQ(input, DeviceOperationStatusCode.INVALID_DEVICE_ID.getMessage(),
                    DeviceOperationStatusCode.INVALID_DEVICE_ID.getCode());

        }
        String osTypeString = deviceRequest.getOsType();
        OSType osType = Helper.enumerateOSType(osTypeString);
        if (osType == null) {
            return IQUtils.createErrorIQ(input, DeviceOperationStatusCode.INVALID_OS_TYPE.getMessage(),
                    DeviceOperationStatusCode.INVALID_OS_TYPE.getCode());
        }

        String pushTypeString = deviceRequest.getPushType();
        if (pushTypeString != null) {
            PushType pushType = Helper.enumeratePushType(pushTypeString);
            if (pushType == null) {
                return IQUtils.createErrorIQ(input, DeviceOperationStatusCode.INVALID_PUSH_TYPE.getMessage(),
                        DeviceOperationStatusCode.INVALID_PUSH_TYPE.getCode());
            }
        }

        //validate phone number
        String phoneNumber = deviceRequest.getPhoneNumber();
        if (phoneNumber != null) {
            boolean valid = Helper.checkPhoneNumber(phoneNumber);
            if (!valid) {
                return IQUtils.createErrorIQ(input, DeviceOperationStatusCode.INVALID_PHONE_NUMBER.getMessage(),
                        DeviceOperationStatusCode.INVALID_PHONE_NUMBER.getCode());
            }
        }

        //validate the carrier information
        String carrierInfo = deviceRequest.getCarrierInfo();
        if (carrierInfo != null) {
            CarrierEnum carrierEnum = Helper.enumerateCarrierInfo(carrierInfo);
            if (carrierEnum == null) {
                return IQUtils.createErrorIQ(input, DeviceOperationStatusCode.INVALID_CARRIER.getMessage(),
                        DeviceOperationStatusCode.INVALID_CARRIER.getCode());
            }
        }
        String appId = app.getAppId();
        if (AlertsUtil.maxDevicesPerAppLimitReached(appId, ownerJid)) {
            DeviceOperationStatusCode code = DeviceOperationStatusCode.DEVICES_PER_APP_EXCEEDED;
            code.setMessage("Reached devices per app limit : "
                    + MMXConfiguration.getConfiguration().getLong(MMXConfigKeys.MAX_DEVICES_PER_APP, -1));
            int limit = AlertsUtil.getMaxDevicePerAppLimit();
            AlertEventsManager.post(new MMXMaxDevicesPerAppLimitReachedEvent(appId, limit));
            return IQUtils.createErrorIQ(input, code.getMessage(), code.getCode());
        }

        return null;
    }

    private IQ processGetTags(IQ iq, JID fromJID, AppEntity appEntity, String payload) {
        DeviceDAO deviceDAO = new DeviceDAOImpl(new OpenFireDBConnectionProvider());
        String appId = appEntity.getAppId();
        String userId = JIDUtil.getUserId(fromJID);
        DevId rqt = GsonData.getGson().fromJson(payload, DevId.class);

        DeviceEntity devEntity = deviceDAO.getDeviceUsingId(appId, rqt.getDevId(), DeviceStatus.ACTIVE);
        if (devEntity == null) {
            return IQUtils.createErrorIQ(iq, "Device ID not found", StatusCode.NOT_FOUND);
        }
        if (!userId.equals(devEntity.getOwnerId())) {
            return IQUtils.createErrorIQ(iq, "Not a device owner", StatusCode.FORBIDDEN);
        }

        List<TagEntity> tagEntities = DBUtil.getTagDAO().getTagEntitiesForDevice(devEntity);
        List<String> tags = new ArrayList<String>();
        List<Date> dates = new ArrayList<Date>();
        for (TagEntity tagEntity : tagEntities) {
            tags.add(tagEntity.getTagname());
            dates.add(tagEntity.getCreationDate());
        }

        Collections.sort(dates, Collections.reverseOrder());

        DevTags devTags = new DevTags(rqt.getDevId(), tags, Utils.isNullOrEmpty(dates) ? null : dates.get(0));
        return IQUtils.createResultIQ(iq, devTags.toJson());
    }

    private IQ processSetTags(IQ iq, JID fromJID, AppEntity appEntity, String payload) {
        String appId = appEntity.getAppId();
        String userId = JIDUtil.getUserId(fromJID);
        DevTags rqt = DevTags.fromJson(payload);
        DeviceDAO deviceDAO = new DeviceDAOImpl(new OpenFireDBConnectionProvider());

        DeviceEntity devEntity = deviceDAO.getDeviceUsingId(appId, rqt.getDevId(), DeviceStatus.ACTIVE);
        if (devEntity == null) {
            return IQUtils.createErrorIQ(iq, "Device not found", StatusCode.NOT_FOUND);
        }

        if (!userId.equals(devEntity.getOwnerId())) {
            return IQUtils.createErrorIQ(iq, "Not a device owner", StatusCode.FORBIDDEN);
        }

        TagDAO tagDAO = DBUtil.getTagDAO();
        tagDAO.deleteAllTagsForDevice(devEntity);

        List<String> tagNames = rqt.getTags();

        if (!Utils.isNullOrEmpty(tagNames)) {
            for (String tag : tagNames) {
                try {
                    tagDAO.createDeviceTag(tag, appId, devEntity.getId());
                } catch (DbInteractionException e) {
                    LOGGER.error("processSetTags : unable to set tags for device={}", devEntity);
                    return IQUtils.createErrorIQ(iq, "server error in creating tags", StatusCode.INTERNAL_ERROR);
                }
            }
        }

        MMXStatus status = new MMXStatus().setCode(StatusCode.SUCCESS).setMessage("successfully set tags");
        return IQUtils.createResultIQ(iq, status.toJson());
    }

    private IQ processAddTags(IQ iq, JID fromJID, AppEntity appEntity, String payload) {
        String appId = appEntity.getAppId();
        String userId = JIDUtil.getUserId(fromJID);
        DevTags rqt = DevTags.fromJson(payload);
        DeviceDAO deviceDAO = new DeviceDAOImpl(new OpenFireDBConnectionProvider());

        DeviceEntity devEntity = deviceDAO.getDeviceUsingId(appId, rqt.getDevId(), DeviceStatus.ACTIVE);
        if (devEntity == null) {
            return IQUtils.createErrorIQ(iq, "Device not found", StatusCode.NOT_FOUND);
        }
        if (!userId.equals(devEntity.getOwnerId())) {
            return IQUtils.createErrorIQ(iq, "Not a device owner", StatusCode.FORBIDDEN);
        }

        // Add the tags
        for (String tag : rqt.getTags()) {
            try {
                DBUtil.getTagDAO().createDeviceTag(tag, devEntity.getAppId(), devEntity.getId());
            } catch (IllegalArgumentException e) {
                return IQUtils.createErrorIQ(iq, e.getMessage(), StatusCode.BAD_REQUEST);
            } catch (Exception e) {
                return IQUtils.createErrorIQ(iq, "Device does not exist", StatusCode.FORBIDDEN);
            }
        }

        MMXStatus status = new MMXStatus().setCode(StatusCode.SUCCESS).setMessage("successfully added tags");
        return IQUtils.createResultIQ(iq, status.toJson());
    }

    private IQ processRemoveTags(IQ iq, JID fromJID, AppEntity appEntity, String payload) {
        String appId = appEntity.getAppId();
        String userId = JIDUtil.getUserId(fromJID);
        DevTags rqt = DevTags.fromJson(payload);
        DeviceDAO deviceDAO = new DeviceDAOImpl(new OpenFireDBConnectionProvider());

        DeviceEntity devEntity = deviceDAO.getDeviceUsingId(appId, rqt.getDevId(), DeviceStatus.ACTIVE);
        if (devEntity == null) {
            return IQUtils.createErrorIQ(iq, "Device not found", StatusCode.NOT_FOUND);
        }
        if (!userId.equals(devEntity.getOwnerId())) {
            return IQUtils.createErrorIQ(iq, "Not a device owner", StatusCode.FORBIDDEN);
        }

        TagDAO tagDAO = DBUtil.getTagDAO();
        if (!Utils.isNullOrEmpty(rqt.getTags()))
            tagDAO.deleteTagsForDevice(devEntity, rqt.getTags());

        MMXStatus status = new MMXStatus().setCode(StatusCode.SUCCESS).setMessage("successfully removed tags");
        return IQUtils.createResultIQ(iq, status.toJson());
    }

    /**
     * Validate the un reg request.
     * @param input
     * @param deviceRequest
     * @return a non null IQ if the validation fails
     */
    private IQ validateRequest(IQ input, DevId deviceRequest) {
        String deviceId = deviceRequest.getDevId();
        if (deviceId == null || deviceId.isEmpty()) {
            return IQUtils.createErrorIQ(input, DeviceOperationStatusCode.INVALID_DEVICE_ID.getMessage(),
                    DeviceOperationStatusCode.INVALID_DEVICE_ID.getCode());
        }
        return null;
    }

    private IQ validateRequest(IQ input, String userId) {
        if (userId == null || userId.isEmpty()) {
            return IQUtils.createErrorIQ(input, DeviceOperationStatusCode.INVALID_USER_ID.getMessage(),
                    DeviceOperationStatusCode.INVALID_USER_ID.getCode());
        }
        return null;
    }

    /**
     * Enum for the status codes
     */
    public static enum DeviceOperationStatusCode {
        DEVICE_REGISTERED(201, "Device registered"), DEVICE_UNREGISTERED(200,
                "Device unregistered"), INVALID_APP_ID(400, "Invalid app ID"), INVALID_API_KEY(400,
                        "Invalid api key"), INVALID_USER_ID(400, "Invalid user ID"), INVALID_DEVICE_ID(400,
                                "Invalid deviceId"), INVALID_OS_TYPE(400, "Invalid osType"), INVALID_PUSH_TYPE(400,
                                        "Invalid pushType"), INVALID_COMMAND_VALUE(400,
                                                "Invalid command value"), INVALID_PHONE_NUMBER(400,
                                                        "Invalid phone number"), INVALID_CARRIER(400,
                                                                "Invalid carrier"), DEVICES_PER_APP_EXCEEDED(400,
                                                                        ""), DEVICES_PER_USER_EXCEEDED(400, "");

        private int code;
        private String message;

        DeviceOperationStatusCode(int c, String m) {
            code = c;
            message = m;
        }

        public int getCode() {
            return code;
        }

        public String getMessage() {
            return message;
        }

        public void setMessage(String message) {
            this.message = message;
        }
    }
}