/******************************************************************************* * 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.call; import java.io.IOException; import org.eclipse.kura.KuraConnectException; import org.eclipse.kura.KuraErrorCode; import org.eclipse.kura.KuraException; import org.eclipse.kura.KuraInvalidMessageException; import org.eclipse.kura.KuraStoreException; import org.eclipse.kura.KuraTimeoutException; import org.eclipse.kura.cloud.CloudCallService; import org.eclipse.kura.cloud.app.RequestIdGenerator; import org.eclipse.kura.core.cloud.CloudPayloadProtoBufDecoderImpl; import org.eclipse.kura.core.cloud.CloudPayloadProtoBufEncoderImpl; import org.eclipse.kura.data.DataService; import org.eclipse.kura.data.listener.DataServiceListener; import org.eclipse.kura.message.KuraPayload; import org.eclipse.kura.message.KuraRequestPayload; import org.eclipse.kura.message.KuraResponsePayload; import org.eclipse.kura.message.KuraTopic; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CloudCallServiceImpl implements CloudCallService, DataServiceListener { private static final Logger s_logger = LoggerFactory.getLogger(CloudCallServiceImpl.class); private static RequestIdGenerator s_generator = RequestIdGenerator.getInstance(); private static final int DFLT_PUB_QOS = 0; private static final boolean DFLT_RETAIN = false; private static final int DFLT_PRIORITY = 1; private static final String ACCOUNT_NAME_VAR_NAME = "#account-name"; private static final String CLIENT_ID_VAR_NAME = "#client-id"; private DataService m_dataService; private Object m_lock; private String m_respTopic; private KuraResponsePayload m_resp; // ---------------------------------------------------------------- // // Dependencies // // ---------------------------------------------------------------- public void setDataService(DataService dataService) { this.m_dataService = dataService; } public void unsetDataService(DataService dataService) { this.m_dataService = null; } // ---------------------------------------------------------------- // // Activation APIs // // ---------------------------------------------------------------- protected void activate(ComponentContext componentContext) { s_logger.info("Activating..."); this.m_lock = new Object(); this.m_dataService.addDataServiceListener(this); } protected void deactivate(ComponentContext componentContext) { s_logger.info("Deactivating..."); this.m_dataService.removeDataServiceListener(this); synchronized (this.m_lock) { this.m_lock.notifyAll(); } } @Override public synchronized KuraResponsePayload call(String appId, String appTopic, KuraPayload appPayload, int timeout) throws KuraConnectException, KuraTimeoutException, KuraStoreException, KuraException { return call(CLIENT_ID_VAR_NAME, appId, appTopic, appPayload, timeout); } @Override public synchronized KuraResponsePayload call(String deviceId, String appId, String appTopic, KuraPayload appPayload, int timeout) throws KuraConnectException, KuraTimeoutException, KuraStoreException, KuraException { // Generate the request ID String requestId = s_generator.next(); StringBuilder sbReqTopic = new StringBuilder("$EDC").append("/").append(ACCOUNT_NAME_VAR_NAME).append("/") .append(deviceId).append("/").append(appId).append("/").append(appTopic); StringBuilder sbRespTopic = new StringBuilder("$EDC").append("/").append(ACCOUNT_NAME_VAR_NAME).append("/") .append(CLIENT_ID_VAR_NAME).append("/").append(appId).append("/").append("REPLY").append("/") .append(requestId); KuraRequestPayload req = null; if (appPayload != null) { // Construct a request payload req = new KuraRequestPayload(appPayload); } else { req = new KuraRequestPayload(); } req.setRequestId(requestId); req.setRequesterClientId(CLIENT_ID_VAR_NAME); CloudPayloadProtoBufEncoderImpl encoder = new CloudPayloadProtoBufEncoderImpl(req); byte[] rawPayload; try { rawPayload = encoder.getBytes(); } catch (IOException e) { throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e, "Cannot encode request"); } this.m_respTopic = sbRespTopic.toString(); this.m_resp = null; this.m_dataService.subscribe(this.m_respTopic, 0); synchronized (this.m_lock) { try { this.m_dataService.publish(sbReqTopic.toString(), rawPayload, DFLT_PUB_QOS, DFLT_RETAIN, DFLT_PRIORITY); this.m_lock.wait(timeout); } catch (KuraStoreException e) { throw e; } catch (InterruptedException e) { // Avoid re-throwing this exception which should not normally happen s_logger.warn("Interrupted while waiting for the response"); Thread.interrupted(); } finally { try { this.m_dataService.unsubscribe(this.m_respTopic); } catch (KuraException e) { s_logger.error("Cannot unsubscribe"); } this.m_respTopic = null; } } if (this.m_resp == null) { throw new KuraTimeoutException("Timed out while waiting for the response"); } return this.m_resp; } public void cancel() { synchronized (this.m_lock) { notifyAll(); } } @Override public void onConnectionEstablished() { // Ignore } @Override public void onDisconnecting() { // Ignore } @Override public void onDisconnected() { // Ignore } @Override public void onConnectionLost(Throwable cause) { // Ignore } @Override public void onMessageArrived(String topic, byte[] payload, int qos, boolean retained) { s_logger.debug("Message arrived on topic: '{}'", topic); if (this.m_respTopic != null) { // Filter on application ID and topic KuraTopic kuraTopic = new KuraTopic(topic); KuraTopic kuraRespTopic = new KuraTopic(this.m_respTopic); if (kuraTopic.getApplicationId().equals(kuraRespTopic.getApplicationId()) && kuraTopic.getApplicationTopic().equals(kuraRespTopic.getApplicationTopic())) { s_logger.debug("Got response"); CloudPayloadProtoBufDecoderImpl decoder = new CloudPayloadProtoBufDecoderImpl(payload); KuraResponsePayload resp = null; try { KuraPayload kuraPayload = decoder.buildFromByteArray(); resp = new KuraResponsePayload(kuraPayload); } catch (KuraInvalidMessageException e) { s_logger.error("Cannot decode protobuf", e); } catch (IOException e) { s_logger.error("Cannot decode protobuf", e); } synchronized (this.m_lock) { this.m_resp = resp; // Can be null this.m_lock.notifyAll(); } } } } @Override public void onMessagePublished(int messageId, String topic) { // Ignore } @Override public void onMessageConfirmed(int messageId, String topic) { // Ignore } @Override public boolean isConnected() { return this.m_dataService.isConnected(); } }