/******************************************************************************* * Copyright (c) 2011, 2016 Eurotech and/or its affiliates * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Eurotech *******************************************************************************/ package org.eclipse.kura.core.cloud; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.eclipse.kura.KuraException; import org.eclipse.kura.cloud.CloudClient; import org.eclipse.kura.cloud.CloudClientListener; import org.eclipse.kura.data.DataService; import org.eclipse.kura.message.KuraPayload; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Implementation of the CloudClient interface. */ public class CloudClientImpl implements CloudClient, CloudClientListener { @SuppressWarnings("unused") private static final Logger s_logger = LoggerFactory.getLogger(CloudClientImpl.class); private final String m_applicationId; private final DataService m_dataService; private final CloudServiceImpl m_cloudServiceImpl; private final List<CloudClientListenerAdapter> m_listeners; protected CloudClientImpl(String applicationId, DataService dataService, CloudServiceImpl cloudServiceImpl) { this.m_applicationId = applicationId; this.m_dataService = dataService; this.m_cloudServiceImpl = cloudServiceImpl; this.m_listeners = new CopyOnWriteArrayList<CloudClientListenerAdapter>(); } /** * Returns the applicationId of this CloudApplicationClient * * @return applicationId */ @Override public String getApplicationId() { return this.m_applicationId; } /** * Releases this CloudClient handle. This instance should no longer be used. * Note: CloudClient does not unsubscribes all subscriptions incurred by this client, * this responsibility is left to the application developer */ @Override public void release() { // remove this from being a callback handler this.m_cloudServiceImpl.removeCloudClient(this); } // -------------------------------------------------------------------- // // CloudCallbackHandler API // // -------------------------------------------------------------------- @Override public void addCloudClientListener(CloudClientListener cloudClientListener) { this.m_listeners.add(new CloudClientListenerAdapter(cloudClientListener)); } @Override public void removeCloudClientListener(CloudClientListener cloudClientListener) { // create a copy to avoid concurrent modification exceptions List<CloudClientListenerAdapter> adapters = new ArrayList<CloudClientListenerAdapter>(this.m_listeners); for (CloudClientListenerAdapter adapter : adapters) { if (adapter.getCloudClientListenerAdapted() == cloudClientListener) { this.m_listeners.remove(adapter); break; } } } // -------------------------------------------------------------------- // // CloudClient API // // -------------------------------------------------------------------- @Override public boolean isConnected() { return this.m_dataService.isConnected(); } @Override public int publish(String topic, KuraPayload payload, int qos, boolean retain) throws KuraException { boolean isControl = false; String appTopic = encodeTopic(topic, isControl); byte[] appPayload = this.m_cloudServiceImpl.encodePayload(payload); return this.m_dataService.publish(appTopic, appPayload, qos, retain, 5); } @Override public int publish(String topic, KuraPayload payload, int qos, boolean retain, int priority) throws KuraException { boolean isControl = false; String appTopic = encodeTopic(topic, isControl); byte[] appPayload = this.m_cloudServiceImpl.encodePayload(payload); return this.m_dataService.publish(appTopic, appPayload, qos, retain, priority); } @Override public int publish(String topic, byte[] payload, int qos, boolean retain, int priority) throws KuraException { boolean isControl = false; String appTopic = encodeTopic(topic, isControl); return this.m_dataService.publish(appTopic, payload, qos, retain, priority); } @Override public int controlPublish(String topic, KuraPayload payload, int qos, boolean retain, int priority) throws KuraException { boolean isControl = true; String appTopic = encodeTopic(topic, isControl); byte[] appPayload = this.m_cloudServiceImpl.encodePayload(payload); return this.m_dataService.publish(appTopic, appPayload, qos, retain, priority); } @Override public int controlPublish(String deviceId, String topic, KuraPayload payload, int qos, boolean retain, int priority) throws KuraException { boolean isControl = true; String appTopic = encodeTopic(deviceId, topic, isControl); byte[] appPayload = this.m_cloudServiceImpl.encodePayload(payload); return this.m_dataService.publish(appTopic, appPayload, qos, retain, priority); } @Override public int controlPublish(String deviceId, String topic, byte[] payload, int qos, boolean retain, int priority) throws KuraException { boolean isControl = true; String appTopic = encodeTopic(deviceId, topic, isControl); return this.m_dataService.publish(appTopic, payload, qos, retain, priority); } @Override public void subscribe(String topic, int qos) throws KuraException { boolean isControl = false; String appTopic = encodeTopic(topic, isControl); this.m_dataService.subscribe(appTopic, qos); } @Override public void controlSubscribe(String topic, int qos) throws KuraException { boolean isControl = true; String appTopic = encodeTopic(topic, isControl); this.m_dataService.subscribe(appTopic, qos); } @Override public void unsubscribe(String topic) throws KuraException { boolean isControl = false; String appTopic = encodeTopic(topic, isControl); this.m_dataService.unsubscribe(appTopic); } @Override public void controlUnsubscribe(String topic) throws KuraException { boolean isControl = true; String appTopic = encodeTopic(topic, isControl); this.m_dataService.unsubscribe(appTopic); } @Override public List<Integer> getUnpublishedMessageIds() throws KuraException { String topicRegex = getAppTopicRegex(); return this.m_dataService.getUnpublishedMessageIds(topicRegex); } @Override public List<Integer> getInFlightMessageIds() throws KuraException { String topicRegex = getAppTopicRegex(); return this.m_dataService.getInFlightMessageIds(topicRegex); } @Override public List<Integer> getDroppedInFlightMessageIds() throws KuraException { String topicRegex = getAppTopicRegex(); return this.m_dataService.getDroppedInFlightMessageIds(topicRegex); } // -------------------------------------------------------------------- // // CloudCallbackHandler API // // -------------------------------------------------------------------- @Override public void onMessageArrived(String deviceId, String appTopic, KuraPayload payload, int qos, boolean retain) { for (CloudClientListener listener : this.m_listeners) { listener.onMessageArrived(deviceId, appTopic, payload, qos, retain); } } @Override public void onControlMessageArrived(String deviceId, String appTopic, KuraPayload payload, int qos, boolean retain) { for (CloudClientListener listener : this.m_listeners) { listener.onControlMessageArrived(deviceId, appTopic, payload, qos, retain); } } @Override public void onMessageConfirmed(int pubId, String appTopic) { for (CloudClientListener listener : this.m_listeners) { listener.onMessageConfirmed(pubId, appTopic); } } @Override public void onMessagePublished(int pubId, String appTopic) { for (CloudClientListener listener : this.m_listeners) { listener.onMessagePublished(pubId, appTopic); } } @Override public void onConnectionEstablished() { for (CloudClientListener listener : this.m_listeners) { listener.onConnectionEstablished(); } } @Override public void onConnectionLost() { for (CloudClientListener listener : this.m_listeners) { listener.onConnectionLost(); } } // ---------------------------------------------------------------- // // Private methods // // ---------------------------------------------------------------- private String encodeTopic(String topic, boolean isControl) { CloudServiceOptions options = this.m_cloudServiceImpl.getCloudServiceOptions(); return encodeTopic(options.getTopicClientIdToken(), topic, isControl); } private String encodeTopic(String deviceId, String topic, boolean isControl) { CloudServiceOptions options = this.m_cloudServiceImpl.getCloudServiceOptions(); StringBuilder sb = new StringBuilder(); if (isControl) { sb.append(options.getTopicControlPrefix()).append(options.getTopicSeparator()); } sb.append(options.getTopicAccountToken()).append(options.getTopicSeparator()).append(deviceId) .append(options.getTopicSeparator()).append(this.m_applicationId); if (topic != null && !topic.isEmpty()) { sb.append(options.getTopicSeparator()).append(topic); } return sb.toString(); } private String getAppTopicRegex() { CloudServiceOptions options = this.m_cloudServiceImpl.getCloudServiceOptions(); StringBuilder sb = new StringBuilder(); // String regexExample = "^(\\$EDC/)?eurotech/.+/conf-v1(/.+)?"; // Optional control prefix sb.append("^(") // .append(options.getTopicControlPrefix()) .append("\\$EDC").append(options.getTopicSeparator()).append(")?") .append(options.getTopicAccountToken()).append(options.getTopicSeparator()).append(".+") // Any device ID .append(options.getTopicSeparator()).append(this.m_applicationId).append("(/.+)?"); return sb.toString(); } }