├── src ├── test │ ├── resources │ │ ├── .gitkeep │ │ └── device-mqtt.raml │ └── java │ │ └── org │ │ └── edgexfoundry │ │ ├── TestMqttDeviceSendor.java │ │ └── TestMqttDeviceCommandRec.java └── main │ ├── resources │ ├── banner.txt │ ├── bootstrap.properties │ ├── watcher.properties │ ├── schedule.properties │ ├── spring-config.xml │ ├── rest-endpoint.properties │ └── application.properties │ └── java │ └── org │ └── edgexfoundry │ ├── mqtt │ ├── messaging │ │ ├── MessageProcessor.java │ │ ├── IncomingMessageProcessor.java │ │ ├── CommandResponseMessageProcessor.java │ │ ├── OutgoingSender.java │ │ ├── CommandResponseListener.java │ │ └── IncomingListener.java │ ├── ResponseTask.java │ ├── ObjectTransform.java │ └── DeviceDiscovery.java │ ├── domain │ ├── ScanList.java │ ├── CmdMsg.java │ ├── ResponseObject.java │ ├── MqttObject.java │ ├── MqttAttribute.java │ ├── Transaction.java │ ├── SimpleSchedule.java │ ├── SimpleScheduleEvent.java │ └── SimpleWatcher.java │ ├── HeartBeat.java │ ├── controller │ ├── StatusController.java │ ├── ServiceController.java │ ├── LocalErrorController.java │ ├── CommandController.java │ └── UpdateController.java │ ├── Initializer.java │ ├── handler │ ├── UpdateHandler.java │ ├── CommandHandler.java │ └── CoreDataMessageHandler.java │ ├── Application.java │ ├── scheduling │ └── Scheduler.java │ ├── data │ ├── WatcherStore.java │ ├── ObjectStore.java │ ├── ProfileStore.java │ └── DeviceStore.java │ └── BaseService.java ├── .settings ├── org.eclipse.m2e.core.prefs ├── org.eclipse.core.resources.prefs └── org.eclipse.jdt.core.prefs ├── README.md ├── docker-files ├── README.md ├── bootstrap.properties ├── Dockerfile ├── Dockerfile.aarch64 ├── pom.xml ├── MQTTTestDeviceProfile.yml └── application.properties ├── .project ├── .gitignore ├── Attribution.txt ├── .classpath ├── ReleaseNotes.md ├── MQTTTestDeviceProfile.yml ├── pom.xml └── LICENSE-2.0.txt /src/test/resources/.gitkeep: -------------------------------------------------------------------------------- 1 | placeholder to keep git from removing the directory -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Main Author: Jim White 2 | 3 | Copyright 2016-17, Dell, Inc. 4 | 5 | MQTT Micro Service - device service for connecting an MQTT topic to EdgeX acting like a device/sensor feed. 6 | 7 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding//src/main/resources=UTF-8 4 | encoding//src/test/java=UTF-8 5 | encoding//src/test/resources=UTF-8 6 | encoding/=UTF-8 7 | -------------------------------------------------------------------------------- /docker-files/README.md: -------------------------------------------------------------------------------- 1 | Copyright 2016-17, Dell, Inc. 2 | 3 | This project is automatically populated by the EdgeX Foundry Build Process. It contains the Dockerfile and other files needed to create the Docker image for the EdgeX Foundry MQTT Device Micro Service. 4 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 3 | org.eclipse.jdt.core.compiler.compliance=1.8 4 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 5 | org.eclipse.jdt.core.compiler.source=1.8 6 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ___ _ __ __ ___ _ __ __ ___ _____ _____ 2 | | __|__| |__ _ ___\ \/ / ___ | \ _____ _(_)__ ___ | \/ |/ _ \_ _|_ _| 3 | | _|/ _` / _` / -_)> < |___| | |) / -_) V / / _/ -_) | |\/| | (_) || | | | 4 | |___\__,_\__, \___/_/\_\ |___/\___|\_/|_\__\___| |_| |_|\__\_\|_| |_| 5 | |___/ 6 | 7 | /******************************************************************************* 8 | * Copyright 2017, Dell, Inc. All Rights Reserved. 9 | ******************************************************************************/ 10 | 11 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | device-mqtt 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.sonarlint.eclipse.core.sonarlintBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.m2e.core.maven2Builder 20 | 21 | 22 | 23 | 24 | 25 | org.eclipse.jdt.core.javanature 26 | org.eclipse.m2e.core.maven2Nature 27 | 28 | 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### /.gitignore-boilerplates/Global/Eclipse.gitignore 2 | *.pydevproject 3 | .metadata 4 | bin/ 5 | tmp/** 6 | tmp/**/* 7 | *.tmp 8 | *.bak 9 | *.swp 10 | *~.nib 11 | local.properties 12 | .loadpath 13 | 14 | # External tool builders 15 | .externalToolBuilders/ 16 | 17 | # Locally stored "Eclipse launch configurations" 18 | *.launch 19 | 20 | # CDT-specific 21 | .cproject 22 | 23 | # PDT-specific 24 | .buildpath 25 | 26 | ### .gitignore-boilerplates/Global/OSX.gitignore 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | Icon 31 | 32 | # Thumbnails 33 | ._* 34 | 35 | # Files that might appear on external disk 36 | .Spotlight-V100 37 | .Trashes 38 | 39 | ### .gitignore-boilerplates/Java.gitignore 40 | *.class 41 | 42 | # Package Files # 43 | *.jar 44 | *.war 45 | *.ear 46 | 47 | # Intellij 48 | .idea/ 49 | *.iml 50 | *.iws 51 | 52 | # Maven 53 | log/ 54 | target/ 55 | 56 | # MQTT test runs 57 | Northbound*/ 58 | /iot-core-data.log 59 | -------------------------------------------------------------------------------- /src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Copyright 2016-2017 Dell Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # @microservice: device-mqtt 17 | # @author: Jim White, Dell 18 | # @version: 1.0.0 19 | ############################################################################### 20 | spring.application.name=device-mqtt 21 | #spring.cloud.consul.host= 22 | spring.cloud.consul.config.profileSeparator=; 23 | spring.cloud.consul.enabled=false 24 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/mqtt/messaging/MessageProcessor.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.mqtt.messaging; 20 | 21 | public interface MessageProcessor { 22 | 23 | void process(byte[] messagePayload); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /docker-files/bootstrap.properties: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Copyright 2016-2017 Dell Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # @microservice: device-mqtt 17 | # @author: Jim White, Dell 18 | # @version: 1.0.0 19 | ############################################################################### 20 | spring.application.name=edgex-device-mqtt 21 | spring.cloud.consul.host=edgex-core-consul 22 | spring.cloud.consul.port=8500 23 | spring.cloud.consul.config.profileSeparator=; 24 | spring.cloud.consul.enabled=true 25 | spring.profiles.active=docker -------------------------------------------------------------------------------- /src/main/resources/watcher.properties: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Copyright 2016-2017 Dell Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # @microservice: device-mqtt 17 | # @author: Jim White, Dell 18 | # @version: 1.0.0 19 | ############################################################################### 20 | # Add comma separated watcher initializations, may be partially specified, used by SimpleWatcher for initialization 21 | #default.watcher.name=Sample 22 | #default.watcher.service=device-mqtt 23 | #default.watcher.profile=JC.RR5.NAE9.ConfRoom.Padre.Island 24 | #default.watcher.name_identifiers=[246] 25 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/domain/ScanList.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.domain; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | public class ScanList { 26 | private List> scan = new ArrayList<>(); 27 | 28 | public List> getScan() { 29 | return scan; 30 | } 31 | 32 | public void setScan(List> scan) { 33 | this.scan = scan; 34 | } 35 | 36 | public void add(Map device) { 37 | scan.add(device); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Attribution.txt: -------------------------------------------------------------------------------- 1 | The following open source projects are referenced by the MQTT Device Service: 2 | 3 | Spring Boot (Apache 2.0) - https://projects.spring.io/spring-boot/ 4 | https://github.com/spring-projects/spring-boot/blob/master/LICENSE.txt 5 | 6 | Spring Cloud (Apache 2.0) - http://projects.spring.io/spring-cloud/ 7 | https://github.com/spring-cloud/spring-cloud-release/blob/master/LICENSE.txt 8 | 9 | Spring Framework (Apache 2.0) - http://projects.spring.io/spring-framework/ 10 | https://github.com/spring-projects/spring-framework 11 | 12 | Log4J (Apache 2.0) - https://logging.apache.org/log4j/2.x/ 13 | https://logging.apache.org/log4j/2.0/license.html 14 | 15 | Junit (EPL 1.0) - http://junit.org/junit4/ 16 | http://junit.org/junit4/license.html 17 | 18 | GSON (Apache 2.0) - https://github.com/google/gson 19 | https://github.com/google/gson/blob/master/LICENSE 20 | 21 | Resteasy, JBoss (Apache 2.0) - http://resteasy.jboss.org/ 22 | https://github.com/resteasy/Resteasy/blob/master/License.html 23 | 24 | Apache Commons Lang version 3.0 (Apache 2.0) - https://commons.apache.org/proper/commons-lang/ 25 | https://commons.apache.org/proper/commons-lang/ 26 | 27 | Snakeyaml (Apache 2.0) - https://bitbucket.org/asomov/snakeyaml 28 | https://bitbucket.org/asomov/snakeyaml/src/f563667f64835b7d9461b8b4cc7487dbb2f9faf5/LICENSE.txt 29 | 30 | Eclipse Paho Java Client (EPL 1.0) - https://eclipse.org/paho/clients/java/ 31 | https://projects.eclipse.org/projects/technology.paho -------------------------------------------------------------------------------- /src/main/resources/schedule.properties: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Copyright 2016-2017 Dell Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # @microservice: device-mqtt 17 | # @author: Jim White, Dell 18 | # @version: 1.0.0 19 | ############################################################################### 20 | #-----------Scheduler Config-------------------- 21 | # schedule interval in ms 22 | schedule.interval=500 23 | 24 | # Add comma separated schedule and scheduleevent initializations, may be partially specified, used by SimpleSchedule and SimpleScheduleEvent for initialization 25 | #default.schedule.name=Interval-15s 26 | #default.schedule.frequency=PT15S 27 | # 28 | #default.scheduleEvent.name=device-mqtt-Discovery 29 | #default.scheduleEvent.path=/api/v1/discovery 30 | #default.scheduleEvent.service=device-mqtt 31 | #default.scheduleEvent.schedule=Interval-15s 32 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/mqtt/ResponseTask.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.mqtt; 20 | 21 | import java.util.concurrent.Callable; 22 | 23 | import org.edgexfoundry.mqtt.messaging.CommandResponseMessageProcessor; 24 | 25 | public class ResponseTask implements Callable { 26 | 27 | private CommandResponseMessageProcessor processor; 28 | private String uuid; 29 | 30 | public ResponseTask(String uuid, CommandResponseMessageProcessor processor) { 31 | this.uuid = uuid; 32 | this.processor = processor; 33 | } 34 | 35 | @Override 36 | public String call() throws Exception { 37 | return processor.getResponse(uuid); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/HeartBeat.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry; 20 | 21 | import org.edgexfoundry.support.logging.client.EdgeXLogger; 22 | import org.edgexfoundry.support.logging.client.EdgeXLoggerFactory; 23 | import org.springframework.scheduling.annotation.EnableScheduling; 24 | import org.springframework.scheduling.annotation.Scheduled; 25 | 26 | @EnableScheduling 27 | public class HeartBeat { 28 | 29 | private static final EdgeXLogger logger = EdgeXLoggerFactory.getEdgeXLogger(HeartBeat.class); 30 | 31 | @Scheduled(fixedRateString = "${heart.beat.time}") 32 | public void pulse() { 33 | logger.info("This is the device-mqtt device service."); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/mqtt/messaging/IncomingMessageProcessor.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.mqtt.messaging; 20 | 21 | import org.edgexfoundry.handler.MqttHandler; 22 | import org.springframework.beans.factory.annotation.Autowired; 23 | import org.springframework.stereotype.Component; 24 | 25 | @Component 26 | public class IncomingMessageProcessor implements MessageProcessor { 27 | 28 | @Autowired 29 | MqttHandler handler; 30 | 31 | @Override 32 | public void process(byte[] messagePayload) { 33 | 34 | String json = new String(messagePayload); 35 | if (json != null && json.length() > 0) { 36 | handler.processJson(json); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/domain/CmdMsg.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.domain; 20 | 21 | import java.util.UUID; 22 | 23 | @SuppressWarnings("unused") 24 | public class CmdMsg { 25 | 26 | private String cmd; 27 | private String method; 28 | private String uuid; 29 | private String param; 30 | 31 | public CmdMsg(String cmd, String method) { 32 | this.cmd = cmd; 33 | this.method = method; 34 | this.uuid = UUID.randomUUID().toString(); 35 | } 36 | 37 | public CmdMsg(String cmd, String method, String param) { 38 | this(cmd, method); 39 | this.param = param; 40 | } 41 | 42 | public String getUuid() { 43 | return uuid; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /docker-files/Dockerfile: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Copyright 2016-2017 Dell Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | ############################################################################### 17 | FROM maven:3.3-jdk-8-alpine 18 | 19 | COPY docker-files/pom.xml . 20 | 21 | RUN mvn dependency:copy -q 22 | 23 | FROM alpine:3.6 24 | MAINTAINER Jim White 25 | 26 | RUN apk --update add openjdk8-jre 27 | 28 | # environment variables 29 | ENV APP_DIR=/edgex/edgex-device-mqtt 30 | ENV APP=device-mqtt.jar 31 | ENV APP_PORT=49982 32 | 33 | #copy JAR and property files to the image 34 | COPY --from=0 *.jar $APP_DIR/$APP 35 | COPY docker-files/*.properties $APP_DIR/ 36 | #copy Device YML to the image 37 | COPY *.yml $APP_DIR/ 38 | 39 | #expose core data port 40 | EXPOSE $APP_PORT 41 | 42 | #set the working directory 43 | WORKDIR $APP_DIR 44 | 45 | #kick off the micro service 46 | ENTRYPOINT java -jar -Djava.security.egd=file:/dev/urandom -Xmx100M $APP 47 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/domain/ResponseObject.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.domain; 20 | 21 | public class ResponseObject { 22 | 23 | private String name; 24 | private String value; 25 | 26 | public ResponseObject(String name, String value) { 27 | this.name = name; 28 | this.value = value; 29 | } 30 | 31 | public String getName() { 32 | return name; 33 | } 34 | 35 | public void setName(String name) { 36 | this.name = name; 37 | } 38 | 39 | public String getValue() { 40 | return value; 41 | } 42 | 43 | public void setValue(String value) { 44 | this.value = value; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "{\"" + name + "\":\"" + value + "\"}"; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /docker-files/Dockerfile.aarch64: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Copyright 2016-2017 Cavium. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | ############################################################################### 17 | FROM arm64v8/alpine:3.6 18 | MAINTAINER Federico Claramonte 19 | 20 | RUN apk --update add openjdk8-jre maven 21 | 22 | COPY docker-files/pom.xml . 23 | 24 | RUN mvn dependency:copy -q 25 | 26 | FROM arm64v8/alpine:3.6 27 | 28 | RUN apk --update add openjdk8-jre 29 | 30 | # environment variables 31 | ENV APP_DIR=/edgex/edgex-device-mqtt 32 | ENV APP=device-mqtt.jar 33 | ENV APP_PORT=49982 34 | 35 | #copy JAR and property files to the image 36 | COPY --from=0 *.jar $APP_DIR/$APP 37 | COPY docker-files/*.properties $APP_DIR/ 38 | #copy Device YML to the image 39 | COPY *.yml $APP_DIR/ 40 | 41 | #expose core data port 42 | EXPOSE $APP_PORT 43 | 44 | #set the working directory 45 | WORKDIR $APP_DIR 46 | 47 | #kick off the micro service 48 | ENTRYPOINT java -jar -Djava.security.egd=file:/dev/urandom -Xmx100M $APP 49 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/controller/StatusController.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.controller; 20 | 21 | import org.edgexfoundry.support.logging.client.EdgeXLogger; 22 | import org.edgexfoundry.support.logging.client.EdgeXLoggerFactory; 23 | import org.springframework.web.bind.annotation.RequestMapping; 24 | import org.springframework.web.bind.annotation.RequestMethod; 25 | import org.springframework.web.bind.annotation.ResponseBody; 26 | import org.springframework.web.bind.annotation.RestController; 27 | 28 | @RestController 29 | @RequestMapping(value = {"/api/v1/ping", "/api/v1/status"}) 30 | public class StatusController { 31 | 32 | private static final EdgeXLogger logger = 33 | EdgeXLoggerFactory.getEdgeXLogger(StatusController.class); 34 | 35 | @RequestMapping(method = RequestMethod.GET) 36 | public @ResponseBody String ping() { 37 | logger.debug("Device service pinged - yes its up!"); 38 | return "pong"; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/domain/MqttObject.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.domain; 20 | 21 | import org.edgexfoundry.domain.meta.DeviceObject; 22 | 23 | @SuppressWarnings("serial") 24 | public class MqttObject extends DeviceObject { 25 | 26 | private MqttAttribute attributes; 27 | 28 | public MqttObject(DeviceObject object) { 29 | this.setName(object.getName()); 30 | this.setTag(object.getTag()); 31 | this.setDescription(object.getDescription()); 32 | this.setProperties(object.getProperties()); 33 | this.setAttributes(new MqttAttribute(object.getAttributes())); 34 | } 35 | 36 | @Override 37 | public MqttAttribute getAttributes() { 38 | return attributes; 39 | } 40 | 41 | public void setAttributes(MqttAttribute attributes) { 42 | this.attributes = attributes; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return "MqttObject [ " + super.toString() + ", attributes=" + attributes + "]"; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/resources/spring-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 21 | 22 | 32 | 33 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/Initializer.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry; 20 | 21 | import org.edgexfoundry.data.DeviceStore; 22 | import org.edgexfoundry.scheduling.Scheduler; 23 | import org.edgexfoundry.support.logging.client.EdgeXLogger; 24 | import org.edgexfoundry.support.logging.client.EdgeXLoggerFactory; 25 | import org.springframework.beans.factory.annotation.Autowired; 26 | import org.springframework.stereotype.Component; 27 | 28 | @Component 29 | public class Initializer extends BaseService { 30 | 31 | private static final EdgeXLogger logger = EdgeXLoggerFactory.getEdgeXLogger(Initializer.class); 32 | 33 | @Autowired 34 | DeviceStore devices; 35 | 36 | @Autowired 37 | Scheduler schedules; 38 | 39 | @Override 40 | public boolean initialize(String deviceServiceId) { 41 | // load the devices in cache. 42 | devices.initialize(deviceServiceId); 43 | schedules.initialize(getServiceName()); 44 | logger.info("Initialized device service successfully"); 45 | return true; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ReleaseNotes.md: -------------------------------------------------------------------------------- 1 | # v0.2 (10/20/2017) 2 | # Release Notes 3 | 4 | ## Notable Changes 5 | The Barcelona Release (v 0.2) of the MQTT micro service includes the following: 6 | * Application of Google Style Guidelines to the code base 7 | * POM changes for appropriate repository information for distribution/repos management, checkstyle plugins, etc. 8 | * Added Dockerfile for creation of micro service targeted for ARM64 9 | * Consolidated Docker properties files to common directory 10 | 11 | ## Bug Fixes 12 | * Fixed Asynchronous data handling 13 | * Fixed Docker service configuration 14 | * Added check for service existence after initialization to Base Service 15 | 16 | - [#9](https://github.com/edgexfoundry/device-mqtt/pull/9) - Adds null check in BaseService contributed by Tyler Cox ([trcox](https://github.com/trcox)) 17 | - [#8](https://github.com/edgexfoundry/device-mqtt/pull/8) - Add snapshots and staging repo definition contributed by Jeremy Phelps ([JPWKU](https://github.com/JPWKU)) 18 | - [#7](https://github.com/edgexfoundry/device-mqtt/pull/7) - Fixes Maven artifact dependency path contributed by Tyler Cox ([trcox](https://github.com/trcox)) 19 | - [#6](https://github.com/edgexfoundry/device-mqtt/pull/6) - Added support for aarch64 arch contributed by ([feclare](https://github.com/feclare)) 20 | - [#5](https://github.com/edgexfoundry/device-mqtt/pull/5) - Fixes Service to run with SDK Updates contributed by Tyler Cox ([trcox](https://github.com/trcox)) 21 | - [#4](https://github.com/edgexfoundry/device-mqtt/pull/4) - Adds Docker build capability contributed by Tyler Cox ([trcox](https://github.com/trcox)) 22 | - [#3](https://github.com/edgexfoundry/device-mqtt/pull/3) - Add distributionManagement for artifact storage contributed by Tyler Cox ([trcox](https://github.com/trcox)) 23 | - [#2](https://github.com/edgexfoundry/device-mqtt/issues/2) - Update to use Google Code Style 24 | - [#1](https://github.com/edgexfoundry/device-mqtt/pull/1) - Contributed Project Fuse source code contributed by Tyler Cox ([trcox](https://github.com/trcox)) 25 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/domain/MqttAttribute.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.domain; 20 | 21 | import org.edgexfoundry.support.logging.client.EdgeXLogger; 22 | import org.edgexfoundry.support.logging.client.EdgeXLoggerFactory; 23 | 24 | import com.google.gson.Gson; 25 | 26 | public class MqttAttribute { 27 | 28 | private static final EdgeXLogger logger = EdgeXLoggerFactory.getEdgeXLogger(MqttAttribute.class); 29 | 30 | // Replace these attributes with the MQTT 31 | // specific metadata needed by the MQTT Driver 32 | 33 | private String name; 34 | 35 | public MqttAttribute(Object attributes) { 36 | try { 37 | Gson gson = new Gson(); 38 | String jsonString = gson.toJson(attributes); 39 | MqttAttribute thisObject = gson.fromJson(jsonString, this.getClass()); 40 | 41 | this.setName(thisObject.getName()); 42 | 43 | } catch (Exception e) { 44 | logger.error("Cannot Construct MqttAttribute: " + e.getMessage()); 45 | } 46 | } 47 | 48 | 49 | public String getName() { 50 | return name; 51 | } 52 | 53 | 54 | public void setName(String name) { 55 | this.name = name; 56 | } 57 | 58 | } 59 | 60 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/handler/UpdateHandler.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.handler; 20 | 21 | import org.edgexfoundry.data.DeviceStore; 22 | import org.edgexfoundry.data.WatcherStore; 23 | import org.springframework.beans.factory.annotation.Autowired; 24 | import org.springframework.stereotype.Service; 25 | 26 | @Service 27 | public class UpdateHandler { 28 | 29 | @Autowired 30 | private WatcherStore watchers; 31 | 32 | @Autowired 33 | private DeviceStore devices; 34 | 35 | public boolean addDevice(String deviceId) { 36 | return devices.add(deviceId); 37 | } 38 | 39 | public boolean updateDevice(String deviceId) { 40 | return devices.update(deviceId); 41 | } 42 | 43 | public boolean deleteDevice(String deviceId) { 44 | return devices.remove(deviceId); 45 | } 46 | 47 | public boolean addWatcher(String provisionWatcher) { 48 | return watchers.add(provisionWatcher); 49 | } 50 | 51 | public boolean removeWatcher(String provisionWatcher) { 52 | return watchers.remove(provisionWatcher); 53 | } 54 | 55 | public boolean updateWatcher(String provisionWatcher) { 56 | return watchers.update(provisionWatcher); 57 | } 58 | 59 | public boolean updateProfile(String profileId) { 60 | return devices.updateProfile(profileId) && watchers.updateProfile(profileId); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/Application.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry; 20 | 21 | import org.springframework.boot.SpringApplication; 22 | import org.springframework.boot.autoconfigure.SpringBootApplication; 23 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 24 | import org.springframework.context.ConfigurableApplicationContext; 25 | import org.springframework.scheduling.annotation.EnableAsync; 26 | 27 | @SpringBootApplication 28 | @EnableAsync 29 | @EnableDiscoveryClient 30 | public class Application { 31 | public static ConfigurableApplicationContext ctx; 32 | 33 | private static boolean connected = true; 34 | 35 | public static void main(String[] args) { 36 | ctx = SpringApplication.run(Application.class, args); 37 | String welcomeMsg = ctx.getEnvironment().getProperty("app.open.msg"); 38 | System.out.println(welcomeMsg); 39 | if (!connected) { 40 | System.out.println("Unable to connect to incoming message queue. Shutting down!"); 41 | ctx.close(); 42 | } 43 | } 44 | 45 | public static void exit(int rc) { 46 | if (ctx != null) { 47 | SpringApplication.exit(ctx, () -> rc); 48 | } else { 49 | System.exit(rc); 50 | } 51 | } 52 | 53 | /* 54 | * Used by the Initializer to signal to shutdown the app if unable to connect to meta data 55 | * 56 | * @param connected 57 | */ 58 | public static void setConnected(boolean c) { 59 | connected = c; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/org/edgexfoundry/TestMqttDeviceSendor.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry; 20 | 21 | import org.edgexfoundry.mqtt.messaging.OutgoingSender; 22 | 23 | /* 24 | * Use this class to generate random numbers and send them into the device service as if a sensor 25 | * was sending the data. Requires the Device Service along with Mongo, Core Data, and Metadata to be 26 | * running 27 | * 28 | */ 29 | public class TestMqttDeviceSendor { 30 | 31 | private static String MQTT_BROKER = "m11.cloudmqtt.com"; 32 | private static String MQTT_PROTOCOL = "tcp"; 33 | private static int MQTT_BROKER_PORT = 12439; 34 | private static String MQTT_CLIENT_ID = "IncomingDataPublisher"; 35 | private static String MQTT_TOPIC = "DataTopic"; 36 | private static String MQTT_USER = "tobeprovided"; 37 | private static String MQTT_PASS = "tobeprovided"; 38 | private static String MSG = "{\"name\":\"TestMQTTDevice\",\"randnum\":\""; 39 | private static int MSG_LAG = 15; // time in seconds 40 | private static double MAX = 10000; 41 | 42 | public static void main(String[] args) throws InterruptedException { 43 | System.out.println("Test MQTT Device Starting"); 44 | String address = MQTT_PROTOCOL + "://" + MQTT_BROKER + ":" + MQTT_BROKER_PORT; 45 | double random; 46 | OutgoingSender sender = 47 | new OutgoingSender(address, MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, MQTT_TOPIC); 48 | while (true) { 49 | random = Math.random() * MAX; 50 | String msg = MSG + random + "\"}"; 51 | sender.sendMessage(msg.getBytes()); 52 | System.out.println("sent: " + random); 53 | Thread.sleep(1000 * MSG_LAG); 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/controller/ServiceController.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.controller; 20 | 21 | import org.edgexfoundry.data.ObjectStore; 22 | import org.edgexfoundry.handler.MqttHandler; 23 | import org.edgexfoundry.support.logging.client.EdgeXLogger; 24 | import org.edgexfoundry.support.logging.client.EdgeXLoggerFactory; 25 | import org.springframework.beans.factory.annotation.Autowired; 26 | import org.springframework.web.bind.annotation.PathVariable; 27 | import org.springframework.web.bind.annotation.RequestMapping; 28 | import org.springframework.web.bind.annotation.RequestMethod; 29 | import org.springframework.web.bind.annotation.ResponseBody; 30 | import org.springframework.web.bind.annotation.RestController; 31 | 32 | @RestController 33 | @RequestMapping("/api/v1/") 34 | public class ServiceController { 35 | 36 | private static final EdgeXLogger logger = 37 | EdgeXLoggerFactory.getEdgeXLogger(ServiceController.class); 38 | 39 | @Autowired 40 | ObjectStore objects; 41 | 42 | @Autowired 43 | MqttHandler handler; 44 | 45 | @RequestMapping(path = "/debug/transformData/{transformData}", method = RequestMethod.GET) 46 | public @ResponseBody String setTransformData(@PathVariable Boolean transformData) { 47 | logger.info("Setting transform data to: " + transformData); 48 | objects.setTransformData(transformData); 49 | return "Set transform data to: " + transformData; 50 | } 51 | 52 | @RequestMapping(path = "/discovery", method = RequestMethod.POST) 53 | public @ResponseBody String doDiscovery() { 54 | logger.info("Running discovery request"); 55 | handler.scan(); 56 | return "Running discovery"; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/domain/Transaction.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.domain; 20 | 21 | import java.util.ArrayList; 22 | import java.util.HashMap; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.UUID; 26 | 27 | import org.edgexfoundry.domain.core.Reading; 28 | 29 | public class Transaction { 30 | private String transactionId; 31 | private List readings; 32 | private Map opIds; 33 | private Boolean finished = true; 34 | 35 | public Transaction() { 36 | setTransactionId(UUID.randomUUID().toString()); 37 | setReadings(new ArrayList()); 38 | opIds = new HashMap(); 39 | } 40 | 41 | private void setReadings(List readings) { 42 | this.readings = readings; 43 | } 44 | 45 | private void setTransactionId(String transactionId) { 46 | this.transactionId = transactionId; 47 | } 48 | 49 | public String newOpId() { 50 | String opId = UUID.randomUUID().toString(); 51 | opIds.put(opId, false); 52 | finished = false; 53 | return opId; 54 | } 55 | 56 | public void finishOp(String opId, List readings) { 57 | addReadings(readings); 58 | opIds.put(opId, true); 59 | 60 | if (!opIds.values().contains(false)) { 61 | finished = true; 62 | } 63 | } 64 | 65 | public Boolean isFinished() { 66 | return finished; 67 | } 68 | 69 | public String getTransactionId() { 70 | return transactionId; 71 | } 72 | 73 | public List getReadings() { 74 | return readings; 75 | } 76 | 77 | private void addReadings(List readings) { 78 | if (readings != null) { 79 | this.readings.addAll(readings); 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/controller/LocalErrorController.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.controller; 20 | 21 | import java.util.Map; 22 | 23 | import javax.servlet.http.HttpServletRequest; 24 | import javax.servlet.http.HttpServletResponse; 25 | 26 | import org.springframework.beans.factory.annotation.Autowired; 27 | import org.springframework.boot.autoconfigure.web.ErrorAttributes; 28 | import org.springframework.boot.autoconfigure.web.ErrorController; 29 | import org.springframework.web.bind.annotation.RequestMapping; 30 | import org.springframework.web.bind.annotation.RestController; 31 | import org.springframework.web.context.request.RequestAttributes; 32 | import org.springframework.web.context.request.ServletRequestAttributes; 33 | 34 | @RestController 35 | public class LocalErrorController implements ErrorController { 36 | private static final String PATH = "/error"; 37 | 38 | @Autowired 39 | private ErrorAttributes errorAttributes; 40 | 41 | @RequestMapping(value = PATH) 42 | String error(HttpServletRequest request, HttpServletResponse response) { 43 | // Appropriate HTTP response code (e.g. 404 or 500) is automatically set 44 | // by Spring. 45 | // Here we just define response body. 46 | Map errorInfo = getErrorAttributes(request, true); 47 | return errorInfo.get("message") + "\n\n" + errorInfo.get("trace"); 48 | } 49 | 50 | @Override 51 | public String getErrorPath() { 52 | return PATH; 53 | } 54 | 55 | private Map getErrorAttributes(HttpServletRequest request, 56 | boolean includeStackTrace) { 57 | RequestAttributes requestAttributes = new ServletRequestAttributes(request); 58 | return errorAttributes.getErrorAttributes(requestAttributes, includeStackTrace); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/controller/CommandController.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.controller; 20 | 21 | import java.util.Map; 22 | import java.util.concurrent.Callable; 23 | 24 | import org.edgexfoundry.handler.CommandHandler; 25 | import org.springframework.beans.factory.annotation.Autowired; 26 | import org.springframework.web.bind.annotation.PathVariable; 27 | import org.springframework.web.bind.annotation.RequestBody; 28 | import org.springframework.web.bind.annotation.RequestMapping; 29 | import org.springframework.web.bind.annotation.RequestMethod; 30 | import org.springframework.web.bind.annotation.RestController; 31 | 32 | @RestController 33 | @RequestMapping("/api/v1/device") 34 | public class CommandController { 35 | 36 | @Autowired 37 | private CommandHandler command; 38 | 39 | @RequestMapping(value = "/{deviceId}/{cmd}", 40 | method = {RequestMethod.PUT, RequestMethod.POST, RequestMethod.GET}) 41 | public Callable> getCommand(@PathVariable String deviceId, 42 | @PathVariable String cmd, @RequestBody(required = false) String arguments) { 43 | Callable> callable = new Callable>() { 44 | @Override 45 | public Map call() throws Exception { 46 | return command.getResponse(deviceId, cmd, arguments); 47 | } 48 | }; 49 | return callable; 50 | } 51 | 52 | @RequestMapping(value = "/all/{cmd}", 53 | method = {RequestMethod.PUT, RequestMethod.POST, RequestMethod.GET}) 54 | public Callable> getCommands(@PathVariable String cmd, 55 | @RequestBody(required = false) String arguments) { 56 | Callable> callable = new Callable>() { 57 | @Override 58 | public Map call() throws Exception { 59 | return command.getResponses(cmd, arguments); 60 | } 61 | }; 62 | return callable; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /docker-files/pom.xml: -------------------------------------------------------------------------------- 1 | 9 | 11 | 4.0.0 12 | org.edgexfoundry 13 | device-mqtt 14 | 0.5.0-SNAPSHOT 15 | device-mqtt 16 | EdgeX Foundry device-mqtt 17 | 18 | 19 | https://nexus.edgexfoundry.org 20 | content/repositories 21 | 22 | 23 | 24 | 25 | 26 | org.apache.maven.plugins 27 | maven-dependency-plugin 28 | 3.0.1 29 | 30 | 31 | 32 | org.edgexfoundry 33 | device-mqtt 34 | 0.5.0-SNAPSHOT 35 | . 36 | device-mqtt.jar 37 | 38 | 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-maven-plugin 44 | 45 | 46 | 47 | 48 | 49 | 50 | staging 51 | EdgeX Staging Repository 52 | ${nexusproxy}/${repobasepath}/staging 53 | 54 | 55 | snapshots 56 | EdgeX Staging Repository 57 | ${nexusproxy}/${repobasepath}/snapshots 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/domain/SimpleSchedule.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.domain; 20 | 21 | import org.springframework.boot.context.properties.ConfigurationProperties; 22 | import org.springframework.stereotype.Component; 23 | 24 | @Component 25 | @ConfigurationProperties(ignoreUnknownFields = true, prefix = "default.schedule") 26 | public class SimpleSchedule { 27 | 28 | private String name; 29 | private String start; 30 | private String end; 31 | private String frequency; 32 | private String cron; 33 | private String runOnce; 34 | 35 | public Integer getSize() { 36 | if (name == null) { 37 | return 0; 38 | } 39 | 40 | if ((name.split(",").length == 1) && name.equals("null")) { 41 | return 0; 42 | } 43 | 44 | return name.split(",").length; 45 | } 46 | 47 | public String[] getName() { 48 | return name.split(","); 49 | } 50 | 51 | public void setName(String name) { 52 | this.name = name; 53 | } 54 | 55 | public String[] getStart() { 56 | if (start == null) { 57 | return new String[getSize()]; 58 | } 59 | 60 | return start.split(","); 61 | } 62 | 63 | public void setStart(String start) { 64 | this.start = start; 65 | } 66 | 67 | public String[] getEnd() { 68 | if (end == null) { 69 | return new String[getSize()]; 70 | } 71 | 72 | return end.split(","); 73 | } 74 | 75 | public void setEnd(String end) { 76 | this.end = end; 77 | } 78 | 79 | public String[] getFrequency() { 80 | if (frequency == null) { 81 | return new String[getSize()]; 82 | } 83 | 84 | return frequency.split(","); 85 | } 86 | 87 | public void setFrequency(String frequency) { 88 | this.frequency = frequency; 89 | } 90 | 91 | public String[] getCron() { 92 | if (cron == null) { 93 | return new String[getSize()]; 94 | } 95 | 96 | return cron.split(","); 97 | } 98 | 99 | public void setCron(String cron) { 100 | this.cron = cron; 101 | } 102 | 103 | public String[] getRunOnce() { 104 | if (runOnce == null) { 105 | return new String[getSize()]; 106 | } 107 | 108 | return runOnce.split(","); 109 | } 110 | 111 | public void setRunOnce(String runOnce) { 112 | this.runOnce = runOnce; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/domain/SimpleScheduleEvent.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.domain; 20 | 21 | import org.springframework.boot.context.properties.ConfigurationProperties; 22 | import org.springframework.stereotype.Component; 23 | 24 | @Component 25 | @ConfigurationProperties(ignoreUnknownFields = true, prefix = "default.scheduleEvent") 26 | public class SimpleScheduleEvent { 27 | 28 | private String name; 29 | private String schedule; 30 | private String parameters; 31 | private String service; 32 | private String path; 33 | private String scheduler; 34 | 35 | public Integer getSize() { 36 | if (name == null) { 37 | return 0; 38 | } 39 | 40 | if ((name.split(",").length == 1) && name.equals("null")) { 41 | return 0; 42 | } 43 | 44 | return name.split(",").length; 45 | } 46 | 47 | public String[] getName() { 48 | return name.split(","); 49 | } 50 | 51 | public void setName(String name) { 52 | this.name = name; 53 | } 54 | 55 | public String[] getSchedule() { 56 | if (schedule == null) { 57 | return new String[getSize()]; 58 | } 59 | 60 | return schedule.split(","); 61 | } 62 | 63 | public void setSchedule(String schedule) { 64 | this.schedule = schedule; 65 | } 66 | 67 | public String[] getParameters() { 68 | if (parameters == null) { 69 | return new String[getSize()]; 70 | } 71 | 72 | return parameters.split(","); 73 | } 74 | 75 | public void setParameters(String parameters) { 76 | this.parameters = parameters; 77 | } 78 | 79 | public String[] getService() { 80 | if (service == null) { 81 | return new String[getSize()]; 82 | } 83 | 84 | return service.split(","); 85 | } 86 | 87 | public void setService(String service) { 88 | this.service = service; 89 | } 90 | 91 | public String[] getPath() { 92 | if (path == null) { 93 | return new String[getSize()]; 94 | } 95 | 96 | return path.split(","); 97 | } 98 | 99 | public void setPath(String path) { 100 | this.path = path; 101 | } 102 | 103 | public String[] getScheduler() { 104 | if (scheduler == null) { 105 | return getService(); 106 | } 107 | 108 | return scheduler.split(","); 109 | } 110 | 111 | public void setScheduler(String scheduler) { 112 | this.scheduler = scheduler; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/resources/rest-endpoint.properties: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Copyright 2016-2017 Dell Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # @microservice: device-mqtt 17 | # @author: Jim White, Dell 18 | # @version: 1.0.0 19 | ############################################################################### 20 | #------------------- REST Endpoints --------------------------------------- 21 | # EdgeX metadata database service connection information 22 | meta.db.addressable.url=http://localhost:48081/api/v1/addressable 23 | meta.db.deviceservice.url=http://localhost:48081/api/v1/deviceservice 24 | meta.db.deviceprofile.url=http://localhost:48081/api/v1/deviceprofile 25 | meta.db.device.url=http://localhost:48081/api/v1/device 26 | meta.db.devicemanager.url=http://localhost:48081/api/v1/devicemanager 27 | meta.db.devicereport.url=http://localhost:48081/api/v1/devicereport 28 | meta.db.command.url=http://localhost:48081/api/v1/command 29 | meta.db.event.url=http://localhost:48081/api/v1/scheduleevent 30 | meta.db.schedule.url=http://localhost:48081/api/v1/schedule 31 | meta.db.provisionwatcher.url=http://localhost:48081/api/v1/provisionwatcher 32 | meta.db.ping.url=http://localhost:48081/api/v1/ping 33 | #meta.db.addressable.url=http://edgex-core-metadata:48081/api/v1/addressable 34 | #meta.db.deviceservice.url=http://edgex-core-metadata:48081/api/v1/deviceservice 35 | #meta.db.deviceprofile.url=http://edgex-core-metadata:48081/api/v1/deviceprofile 36 | #meta.db.device.url=http://edgex-core-metadata:48081/api/v1/device 37 | #meta.db.devicemanager.url=http://edgex-core-metadata:48081/api/v1/devicemanager 38 | #meta.db.devicereport.url=http://edgex-core-metadata:48081/api/v1/devicereport 39 | #meta.db.command.url=http://edgex-core-metadata:48081/api/v1/command 40 | #meta.db.event.url=http://edgex-core-metadata:48081/api/v1/scheduleevent 41 | #meta.db.schedule.url=http://edgex-core-metadata:48081/api/v1/schedule 42 | #meta.db.provisionwatcher.url=http://edgex-core-metadata:48081/api/v1/provisionwatcher 43 | #meta.db.ping.url=http://edgex-core-metadata:48081/api/v1/ping 44 | 45 | #EdgeX core database service connection information 46 | core.db.event.url=http://localhost:48080/api/v1/event 47 | core.db.reading.url=http://localhost:48080/api/v1/reading 48 | core.db.valuedescriptor.url=http://localhost:48080/api/v1/valuedescriptor 49 | core.db.ping.url=http://localhost:48080/api/v1/ping 50 | #core.db.event.url=http://edgex-core-data:48080/api/v1/event 51 | #core.db.reading.url=http://edgex-core-data:48080/api/v1/reading 52 | #core.db.valuedescriptor.url=http://edgex-core-data:48080/api/v1/valuedescriptor 53 | #core.db.ping.url=http://edgex-core-data:48080/api/v1/ping 54 | logging.remote.url=http://localhost:48061/api/v1/logs 55 | #logging.remote.url=http://edgex-support-logging:48061/api/v1/logs 56 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Copyright 2016-2017 Dell Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # @microservice: device-mqtt 17 | # @author: Jim White, Dell 18 | # @version: 1.0.0 19 | ############################################################################### 20 | #REST read data limit 21 | read.max.limit=100 22 | #logging levels (used to control log4j entries) 23 | logging.level.org.springframework=ERROR 24 | logging.level.org.apache=ERROR 25 | logging.level.org.edgexfoundry=DEBUG 26 | app.open.msg=This is the device-mqtt micro service 27 | #every 5 minutes (in milliseconds) 28 | heart.beat.time=300000 29 | server.port=49982 30 | #default device service settings 31 | service.name=device-mqtt 32 | service.host=localhost 33 | #service.host=${service.name} 34 | service.labels=MQTT 35 | service.callback=/api/v1/callback 36 | #connection retry parameters 37 | service.connect.retries=12 38 | service.connect.wait=5000 39 | service.connect.interval=10000 40 | # callback timeout in milliseconds 41 | service.timeout=5000 42 | spring.mvc.dispatch-options-request=true 43 | data.transform=true 44 | mqtt.device.init=Init 45 | mqtt.device.init.args={ value: 1 } 46 | mqtt.device.remove=Remove 47 | mqtt.device.remove.args={ value: 0 } 48 | #----------------------------------- 49 | #Cloud MQTT connection information 50 | #for incoming messages from devices 51 | #Use TestIoTMQTT Broker for testing/dev 52 | INCOMING_MQTT_BROKER_PROTO=tcp 53 | INCOMING_MQTT_BROKER=m11.cloudmqtt.com 54 | INCOMING_MQTT_BROKER_PORT=12439 55 | INCOMING_MQTT_CLIENT_ID=IncomingDataSubscriber 56 | INCOMING_MQTT_TOPIC=DataTopic 57 | INCOMING_MQTT_QOS=0 58 | INCOMING_MQTT_USER=tobeprovided 59 | INCOMING_MQTT_PASS=tobeprovided 60 | #keep alive set to 1 hour 61 | INCOMING_MQTT_KEEP_ALIVE=3600 62 | #for command response messages 63 | RESPONSE_MQTT_BROKER_PROTO=tcp 64 | RESPONSE_MQTT_BROKER=m11.cloudmqtt.com 65 | RESPONSE_MQTT_BROKER_PORT=12439 66 | RESPONSE_MQTT_CLIENT_ID=CommandResponseSubscriber 67 | RESPONSE_MQTT_TOPIC=ResponseTopic 68 | RESPONSE_MQTT_QOS=0 69 | RESPONSE_MQTT_USER=tobeprovided 70 | RESPONSE_MQTT_PASS=tobeprovided 71 | #keep alive set to 1 hour 72 | RESPONSE_MQTT_KEEP_ALIVE=3600 73 | #-----------example test device---------------- 74 | provision.mqtt.device=true 75 | device.profile.name=MQTTTestDeviceProfile.yml 76 | device.name=TestMQTTDevice 77 | device.description=Default Test MQTT Device 78 | device.labels=MQTT, Test 79 | device.addressablename=testMQTTAddressable 80 | #for outgoing test command messages 81 | request.broker.proto=TCP 82 | request.broker=m11.cloudmqtt.com 83 | request.broker.port=12439 84 | request.client.id=OutgoingCommandPublisher 85 | request.topic=CommandTopic 86 | request.user=tobeprovided 87 | request.pass=tobeprovided 88 | 89 | #-----------------Consul Config------------------------------------------ 90 | #The health checking path for Service Registry 91 | spring.cloud.consul.discovery.healthCheckPath=/api/v1/ping 92 | -------------------------------------------------------------------------------- /MQTTTestDeviceProfile.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Dell Inc. All rights reserved. 2 | name: "Test.Device.MQTT.Profile" 3 | manufacturer: "Dell" 4 | model: "MQTT-2" 5 | labels: 6 | - "test" 7 | description: "Test device profile" 8 | deviceResources: 9 | - 10 | name: randnum 11 | description: "device random number" 12 | attributes: 13 | { name: "randnum" } 14 | properties: 15 | value: 16 | { type: "Float", size: "4", readWrite: "R", defaultValue: "0.00", minimum: "100.00", maximum: "0.00" } 17 | units: 18 | { type: "String", readWrite: "R", defaultValue: "" } 19 | - 20 | name: ping 21 | description: "device awake" 22 | attributes: 23 | { name: "ping" } 24 | properties: 25 | value: 26 | { type: "String", size: "0", readWrite: "R", defaultValue: "oops" } 27 | units: 28 | { type: "String", readWrite: "R", defaultValue: "" } 29 | - 30 | name: message 31 | description: "device notification message" 32 | attributes: 33 | { name: "message" } 34 | properties: 35 | value: 36 | { type: "String", size: "0", readWrite: "W" } 37 | units: 38 | { type: "String", readWrite: "R", defaultValue: "" } 39 | 40 | resources: 41 | - 42 | name: randnum 43 | get: 44 | - { index: "1", operation: "get", object: "randnum", property: "value", parameter: "randnum" } 45 | - 46 | name: ping 47 | get: 48 | - { index: "1", operation: "get", object: "ping", property: "value", parameter: "ping" } 49 | - 50 | name: query 51 | get: 52 | - { index: "1", operation: "get", resource: "randnum" } 53 | 54 | commands: 55 | - 56 | name: testrandnum 57 | get: 58 | path: "/api/v1/device/{deviceId}/randnum" 59 | responses: 60 | - 61 | code: "200" 62 | description: "" 63 | expectedValues: ["randnum"] 64 | - 65 | code: "503" 66 | description: "service unavailable" 67 | expectedValues: [] 68 | - 69 | name: testping 70 | get: 71 | path: "/api/v1/device/{deviceId}/ping" 72 | responses: 73 | - 74 | code: "200" 75 | description: "ping the device" 76 | expectedValues: ["ping"] 77 | - 78 | code: "503" 79 | description: "service unavailable" 80 | expectedValues: [] 81 | - 82 | name: testmessage 83 | put: 84 | path: "/api/v1/device/{deviceId}/message" 85 | parameterNames: ["message"] 86 | responses: 87 | - 88 | code: "204" 89 | description: "Set the message." 90 | expectedValues: [] 91 | - 92 | code: "503" 93 | description: "service unavailable" 94 | expectedValues: [] 95 | - 96 | name: testquery 97 | get: 98 | path: "/api/v1/device/{deviceId}/query" 99 | responses: 100 | - 101 | code: "200" 102 | description: "" 103 | expectedValues: ["randnum"] 104 | - 105 | code: "503" 106 | description: "service unavailable" 107 | expectedValues: [] -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/mqtt/messaging/CommandResponseMessageProcessor.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.mqtt.messaging; 20 | 21 | import java.util.Map; 22 | import java.util.concurrent.ConcurrentHashMap; 23 | 24 | import org.apache.log4j.Logger; 25 | import org.springframework.beans.factory.annotation.Value; 26 | import org.springframework.stereotype.Component; 27 | 28 | import com.google.gson.JsonElement; 29 | import com.google.gson.JsonObject; 30 | import com.google.gson.JsonParser; 31 | 32 | @Component 33 | public class CommandResponseMessageProcessor { 34 | 35 | private static final String UUID_KEY = "uuid"; 36 | private static final Logger logger = Logger.getLogger(CommandResponseMessageProcessor.class); 37 | private static final String NO_RESP = "none"; 38 | private static final long SLEEP_TIME = 1000; 39 | 40 | @Value("${service.connect.retries}") 41 | private int retries; 42 | @Value("${service.connect.wait}") 43 | private long delay; 44 | 45 | private Map responses = new ConcurrentHashMap<>(); 46 | 47 | private JsonParser parser = new JsonParser(); 48 | 49 | public void process(byte[] messagePayload) { 50 | String json = new String(messagePayload); 51 | if (json != null && json.length() > 0) { 52 | JsonObject jsonObject = parser.parse(json).getAsJsonObject(); 53 | String uuid = extractCommandData(jsonObject, UUID_KEY); 54 | if (uuid != null) { 55 | responses.put(uuid, json); 56 | logger.debug("Response message for uuid: " + uuid + " stored for processing: " + json); 57 | } else { 58 | logger.error("No UUID found in the message. Response message ignored."); 59 | } 60 | } 61 | } 62 | 63 | public String getResponse(String uuid) { 64 | addResponse(uuid); 65 | logger.debug("Response registered for uuid: " + uuid); 66 | try { 67 | while (NO_RESP.equals(responses.get(uuid))) { 68 | Thread.sleep(SLEEP_TIME); 69 | } 70 | logger.info("Matching response received for uuid: " + uuid); 71 | String response = responses.get(uuid); 72 | responses.remove(uuid); 73 | return response; 74 | } catch (InterruptedException e) { 75 | throw new RuntimeException("Response loop interrupted."); 76 | } 77 | } 78 | 79 | private void addResponse(String uuid) { 80 | responses.put(uuid, NO_RESP); 81 | } 82 | 83 | // get the data out of the MQTT JSON message. 84 | private String extractCommandData(JsonObject jsonObject, String dataPart) { 85 | JsonElement element = jsonObject.get(dataPart); 86 | if (element != null) { 87 | return element.getAsString(); 88 | } else { 89 | return null; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /docker-files/MQTTTestDeviceProfile.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Dell Inc. All rights reserved. 2 | name: "Test.Device.MQTT.Profile" 3 | manufacturer: "Dell" 4 | model: "MQTT-2" 5 | labels: 6 | - "test" 7 | description: "Test device profile" 8 | deviceResources: 9 | - 10 | name: randnum 11 | description: "device random number" 12 | attributes: 13 | { name: "randnum" } 14 | properties: 15 | value: 16 | { type: "Float", size: "4", readWrite: "R", defaultValue: "0.00", minimum: "100.00", maximum: "0.00" } 17 | units: 18 | { type: "String", readWrite: "R", defaultValue: "" } 19 | - 20 | name: ping 21 | description: "device awake" 22 | attributes: 23 | { name: "ping" } 24 | properties: 25 | value: 26 | { type: "String", size: "0", readWrite: "R", defaultValue: "oops" } 27 | units: 28 | { type: "String", readWrite: "R", defaultValue: "" } 29 | - 30 | name: message 31 | description: "device notification message" 32 | attributes: 33 | { name: "message" } 34 | properties: 35 | value: 36 | { type: "String", size: "0", readWrite: "W" } 37 | units: 38 | { type: "String", readWrite: "R", defaultValue: "" } 39 | 40 | resources: 41 | - 42 | name: randnum 43 | get: 44 | - { index: "1", operation: "get", object: "randnum", property: "value", parameter: "randnum" } 45 | - 46 | name: ping 47 | get: 48 | - { index: "1", operation: "get", object: "ping", property: "value", parameter: "ping" } 49 | - 50 | name: query 51 | get: 52 | - { index: "1", operation: "get", resource: "randnum" } 53 | 54 | commands: 55 | - 56 | name: testrandnum 57 | get: 58 | path: "/api/v1/devices/{deviceId}/randnum" 59 | responses: 60 | - 61 | code: "200" 62 | description: "" 63 | expectedValues: ["randnum"] 64 | - 65 | code: "503" 66 | description: "service unavailable" 67 | expectedValues: [] 68 | - 69 | name: testping 70 | get: 71 | path: "/api/v1/devices/{deviceId}/ping" 72 | responses: 73 | - 74 | code: "200" 75 | description: "ping the device" 76 | expectedValues: ["ping"] 77 | - 78 | code: "503" 79 | description: "service unavailable" 80 | expectedValues: [] 81 | - 82 | name: testmessage 83 | put: 84 | path: "/api/v1/devices/{deviceId}/message" 85 | parameterNames: ["message"] 86 | responses: 87 | - 88 | code: "204" 89 | description: "Set the message." 90 | expectedValues: [] 91 | - 92 | code: "503" 93 | description: "service unavailable" 94 | expectedValues: [] 95 | - 96 | name: testquery 97 | get: 98 | path: "/api/v1/devices/{deviceId}/query" 99 | responses: 100 | - 101 | code: "200" 102 | description: "" 103 | expectedValues: ["randnum"] 104 | - 105 | code: "503" 106 | description: "service unavailable" 107 | expectedValues: [] -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/handler/CommandHandler.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.handler; 20 | 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | import java.util.stream.Collectors; 24 | 25 | import org.edgexfoundry.Initializer; 26 | import org.edgexfoundry.data.DeviceStore; 27 | import org.edgexfoundry.domain.meta.Device; 28 | import org.edgexfoundry.exception.controller.LockedException; 29 | import org.edgexfoundry.exception.controller.NotFoundException; 30 | import org.edgexfoundry.support.logging.client.EdgeXLogger; 31 | import org.edgexfoundry.support.logging.client.EdgeXLoggerFactory; 32 | import org.springframework.beans.factory.annotation.Autowired; 33 | import org.springframework.stereotype.Service; 34 | 35 | @Service 36 | public class CommandHandler { 37 | 38 | private static final EdgeXLogger logger = EdgeXLoggerFactory.getEdgeXLogger(CommandHandler.class); 39 | 40 | @Autowired 41 | MqttHandler mqtt; 42 | 43 | @Autowired 44 | DeviceStore devices; 45 | 46 | @Autowired 47 | Initializer init; 48 | 49 | public Map getResponse(String deviceId, String cmd, String arguments) { 50 | if (init.isServiceLocked()) { 51 | logger.error("GET request cmd: " + cmd + " with device service locked on: " + deviceId); 52 | throw new LockedException( 53 | "GET request cmd: " + cmd + " with device service locked on: " + deviceId); 54 | } 55 | 56 | if (devices.isDeviceLocked(deviceId)) { 57 | logger.error("GET request cmd: " + cmd + " with device locked on: " + deviceId); 58 | throw new LockedException("GET request cmd: " + cmd + " with device locked on: " + deviceId); 59 | } 60 | 61 | Device device = devices.getDeviceById(deviceId); 62 | if (mqtt.commandExists(device, cmd)) { 63 | return mqtt.executeCommand(device, cmd, arguments); 64 | } else { 65 | logger.error("Command: " + cmd + " does not exist for device with id: " + deviceId); 66 | throw new NotFoundException("Command", cmd); 67 | } 68 | } 69 | 70 | public Map getResponses(String cmd, String arguments) { 71 | Map responses = new HashMap<>(); 72 | 73 | if (init.isServiceLocked()) { 74 | logger.error("GET request cmd: " + cmd + " with device service locked "); 75 | throw new LockedException("GET request cmd: " + cmd + " with device locked"); 76 | } 77 | 78 | for (String deviceId : devices.getDevices().entrySet().stream().map(d -> d.getValue().getId()) 79 | .collect(Collectors.toList())) { 80 | if (devices.isDeviceLocked(deviceId)) { 81 | continue; 82 | } 83 | 84 | Device device = devices.getDeviceById(deviceId); 85 | if (mqtt.commandExists(device, cmd)) { 86 | responses.putAll(mqtt.executeCommand(device, cmd, arguments)); 87 | } 88 | } 89 | return responses; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/domain/SimpleWatcher.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.domain; 20 | 21 | import java.util.ArrayList; 22 | import java.util.HashMap; 23 | import java.util.List; 24 | import java.util.Map; 25 | 26 | import org.springframework.boot.context.properties.ConfigurationProperties; 27 | import org.springframework.stereotype.Component; 28 | 29 | @Component 30 | @ConfigurationProperties(ignoreUnknownFields = true, prefix = "default.watcher") 31 | public class SimpleWatcher { 32 | 33 | private String name; 34 | private String profile; 35 | private String service; 36 | 37 | // TODO 8: [Optional] For discovery enabled device services: 38 | // Add, delete, or replace the existing identifiers with protocol specific fields 39 | // Sample here is for BLE. Modify the default watchers in watcher.properties as required 40 | // If this is modified, must also add setters for fields, then update the following methods: 41 | // getIdentifier(), getIdentifiers(), toString() 42 | private String nameIdentifiers; 43 | private String macIdentifiers; 44 | 45 | public Integer getSize() { 46 | if (name == null) { 47 | return 0; 48 | } 49 | 50 | if ((name.split(",").length == 1) && name.equals("null")) { 51 | return 0; 52 | } 53 | 54 | return name.split(",").length; 55 | } 56 | 57 | public String[] getName() { 58 | return name.split(","); 59 | } 60 | 61 | public void setName(String name) { 62 | this.name = name; 63 | } 64 | 65 | public void setProfile(String profile) { 66 | this.profile = profile; 67 | } 68 | 69 | public void setService(String service) { 70 | this.service = service; 71 | } 72 | 73 | public Map getIdentifier() { 74 | Map ident = new HashMap(); 75 | 76 | if (nameIdentifiers != null) { 77 | ident.put("name", nameIdentifiers.split(",")); 78 | } 79 | 80 | if (macIdentifiers != null) { 81 | ident.put("address", macIdentifiers.split(",")); 82 | } 83 | 84 | return ident; 85 | } 86 | 87 | public void setNameIdentifiers(String nameIdentifiers) { 88 | this.nameIdentifiers = nameIdentifiers; 89 | } 90 | 91 | public void setMacIdentifiers(String macIdentifiers) { 92 | this.macIdentifiers = macIdentifiers; 93 | } 94 | 95 | public List> getIdentifiers() { 96 | Integer len = this.name.split(",").length; 97 | ArrayList> identifyList = new ArrayList>(); 98 | 99 | for (int i = 0; i < len; i++) { 100 | Map ident = new HashMap(); 101 | 102 | if (nameIdentifiers != null) { 103 | String nameIdent = nameIdentifiers.split(",")[i]; 104 | 105 | if (nameIdent != "") { 106 | ident.put("name", nameIdent); 107 | } 108 | } 109 | 110 | if (macIdentifiers != null) { 111 | String macIdent = macIdentifiers.split(",")[i]; 112 | 113 | if (macIdent != "") { 114 | ident.put("address", macIdent); 115 | } 116 | } 117 | 118 | if (!ident.isEmpty()) { 119 | identifyList.add(ident); 120 | } 121 | } 122 | return identifyList; 123 | } 124 | 125 | public String[] getProfile() { 126 | return profile.split(","); 127 | } 128 | 129 | public String[] getService() { 130 | return service.split(","); 131 | } 132 | 133 | @Override 134 | public String toString() { 135 | return "SimpleWatcher [name=" + name + ", identifiers=" + nameIdentifiers + macIdentifiers 136 | + "]"; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/mqtt/messaging/OutgoingSender.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.mqtt.messaging; 20 | 21 | import org.apache.log4j.Logger; 22 | import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; 23 | import org.eclipse.paho.client.mqttv3.MqttCallback; 24 | import org.eclipse.paho.client.mqttv3.MqttClient; 25 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; 26 | import org.eclipse.paho.client.mqttv3.MqttException; 27 | import org.eclipse.paho.client.mqttv3.MqttMessage; 28 | 29 | public class OutgoingSender implements MqttCallback { 30 | 31 | private static final Logger logger = Logger.getLogger(OutgoingSender.class); 32 | private MqttClient client = null; 33 | 34 | // needed in order to get acknowledgement 35 | private static final int OUTGOING_MQTT_QOS = 0; 36 | private static final int OUTGOING_MQTT_KEEP_ALIVE = 3600; 37 | 38 | private String broker; 39 | private String clientId; 40 | private String user; 41 | private String password; 42 | private String topic; 43 | 44 | public OutgoingSender(String broker, String clientId, String user, String password, 45 | String topic) { 46 | super(); 47 | this.broker = broker; 48 | this.clientId = clientId; 49 | this.user = user; 50 | this.password = password; 51 | this.topic = topic; 52 | this.connectClient(); 53 | } 54 | 55 | public boolean sendMessage(byte[] messagePayload) { 56 | // connectClient(); 57 | if (client != null) { 58 | try { 59 | MqttMessage message = new MqttMessage(messagePayload); 60 | message.setQos(OUTGOING_MQTT_QOS); 61 | message.setRetained(false); 62 | client.publish(topic, message); 63 | return true; 64 | } catch (Exception e) { 65 | logger.error( 66 | "Failed to send outbound message (unexpected issue): " + new String(messagePayload)); 67 | logger.error(e.getLocalizedMessage()); 68 | e.printStackTrace(); 69 | } 70 | } 71 | return false; 72 | 73 | } 74 | 75 | public void setTopic(String topic) { 76 | this.topic = topic; 77 | } 78 | 79 | private void connectClient() { 80 | try { 81 | client = new MqttClient(broker, clientId); 82 | client.setCallback(this); 83 | MqttConnectOptions connOpts = new MqttConnectOptions(); 84 | connOpts.setUserName(user); 85 | connOpts.setPassword(password.toCharArray()); 86 | connOpts.setCleanSession(true); 87 | connOpts.setKeepAliveInterval(OUTGOING_MQTT_KEEP_ALIVE); 88 | logger.debug("Connecting to broker: " + broker); 89 | client.connect(connOpts); 90 | logger.debug("Connected"); 91 | } catch (MqttException e) { 92 | logger.error("Failed to connect to MQTT client ( " + broker + "/" + clientId 93 | + ") for outbound messages"); 94 | logger.error(e.getLocalizedMessage()); 95 | e.printStackTrace(); 96 | } 97 | } 98 | 99 | public void closeClient() { 100 | try { 101 | if (client != null) { 102 | client.disconnect(); 103 | client.close(); 104 | } 105 | } catch (MqttException e) { 106 | logger.error("Problems disconnecting and closing the client."); 107 | logger.error(e.getLocalizedMessage()); 108 | e.printStackTrace(); 109 | } 110 | } 111 | 112 | @Override 113 | public void connectionLost(Throwable cause) { 114 | logger.error("Outgoing Sendor publisher connection lost:" + cause.getLocalizedMessage()); 115 | try { 116 | client.close(); 117 | } catch (MqttException e) { 118 | logger.error("Unable to close the client."); 119 | logger.error(e.getLocalizedMessage()); 120 | e.printStackTrace(); 121 | } 122 | connectClient(); 123 | } 124 | 125 | @Override 126 | public void messageArrived(String topic, MqttMessage message) throws Exception { 127 | logger.error("Message received on Outgoing Sender which should not happen. Payload: " 128 | + message.getPayload().toString()); 129 | } 130 | 131 | @Override 132 | public void deliveryComplete(IMqttDeliveryToken token) { 133 | logger.debug("Message delivered successfully by Outgoing Sender. Token: " + token.toString()); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/scheduling/Scheduler.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.scheduling; 20 | 21 | import javax.ws.rs.ClientErrorException; 22 | 23 | import org.edgexfoundry.controller.AddressableClient; 24 | import org.edgexfoundry.controller.DeviceServiceClient; 25 | import org.edgexfoundry.controller.ScheduleClient; 26 | import org.edgexfoundry.controller.ScheduleEventClient; 27 | import org.edgexfoundry.domain.SimpleSchedule; 28 | import org.edgexfoundry.domain.SimpleScheduleEvent; 29 | import org.edgexfoundry.domain.meta.Addressable; 30 | import org.edgexfoundry.domain.meta.Schedule; 31 | import org.edgexfoundry.domain.meta.ScheduleEvent; 32 | import org.edgexfoundry.support.logging.client.EdgeXLogger; 33 | import org.edgexfoundry.support.logging.client.EdgeXLoggerFactory; 34 | import org.springframework.beans.factory.annotation.Autowired; 35 | import org.springframework.stereotype.Component; 36 | 37 | // TODO: Refactor - Change this to use Quartz or better scheduling mechanism 38 | // TODO: handle clock update 39 | 40 | @Component 41 | public class Scheduler { 42 | private static final EdgeXLogger logger = EdgeXLoggerFactory.getEdgeXLogger(Scheduler.class); 43 | 44 | // Client to fetch schedule events 45 | @Autowired 46 | private ScheduleEventClient scheduleEventClient; 47 | 48 | // Client to fetch schedules 49 | @Autowired 50 | private ScheduleClient scheduleClient; 51 | 52 | 53 | @Autowired 54 | private DeviceServiceClient serviceClient; 55 | 56 | @Autowired 57 | private AddressableClient addressableClient; 58 | 59 | @Autowired 60 | private SimpleSchedule defaultSchedules; 61 | 62 | @Autowired 63 | private SimpleScheduleEvent defaultScheduleEvents; 64 | 65 | 66 | // Scheduler implementation of initialize 67 | public boolean initialize(String serviceName) { 68 | boolean loaded = true; 69 | 70 | addDefaultSchedules(); 71 | addDefaultScheduleEvents(); 72 | logger.info("loaded schedules"); 73 | return loaded; 74 | } 75 | 76 | public void addDefaultSchedules() { 77 | 78 | for (int i = 0; i < defaultSchedules.getSize(); i++) { 79 | String name = defaultSchedules.getName()[i]; 80 | String start = defaultSchedules.getStart()[i]; 81 | String end = defaultSchedules.getEnd()[i]; 82 | String frequency = defaultSchedules.getFrequency()[i]; 83 | String cron = defaultSchedules.getCron()[i]; 84 | Boolean runOnce = Boolean.valueOf(defaultSchedules.getRunOnce()[i]); 85 | Schedule schedule = new Schedule(name, start, end, frequency, cron, runOnce); 86 | 87 | try { 88 | scheduleClient.add(schedule); 89 | } catch (ClientErrorException e) { 90 | // Ignore if the schedule is already present 91 | // schedule.setId(scheduleClient.scheduleForName(name).getId()); 92 | // scheduleClient.update(schedule); 93 | } 94 | } 95 | 96 | } 97 | 98 | public void addDefaultScheduleEvents() { 99 | for (int i = 0; i < defaultScheduleEvents.getSize(); i++) { 100 | String name = defaultScheduleEvents.getName()[i]; 101 | String path = defaultScheduleEvents.getPath()[i]; 102 | String service = defaultScheduleEvents.getService()[i]; 103 | 104 | Addressable serviceAddressable = serviceClient.deviceServiceForName(service).getAddressable(); 105 | Addressable addressable = serviceAddressable; 106 | addressable.setName("Schedule-" + name); 107 | addressable.setPath(path); 108 | addressable.setId(null); 109 | 110 | try { 111 | addressable.setId(addressableClient.add(addressable)); 112 | } catch (ClientErrorException e) { 113 | // Ignore if the addressable is already present 114 | // addressable.setId(addressableClient.addressableForName(addressable.getName()).getId()); 115 | // addressableClient.update(addressable); 116 | } 117 | 118 | String parameters = defaultScheduleEvents.getParameters()[i]; 119 | String schedule = defaultScheduleEvents.getSchedule()[i]; 120 | String scheduler = defaultScheduleEvents.getScheduler()[i]; 121 | 122 | ScheduleEvent scheduleEvent = 123 | new ScheduleEvent(name, addressable, parameters, schedule, scheduler); 124 | 125 | try { 126 | scheduleEventClient.add(scheduleEvent); 127 | } catch (ClientErrorException e) { 128 | // Ignore if the event is already present 129 | // scheduleEvent.setId(scheduleEventClient.scheduleEventForName(name).getId()); 130 | // scheduleEventClient.update(scheduleEvent); 131 | } 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/mqtt/ObjectTransform.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.mqtt; 20 | 21 | import java.math.BigInteger; 22 | 23 | import org.edgexfoundry.data.ObjectStore; 24 | import org.edgexfoundry.domain.meta.PropertyValue; 25 | import org.springframework.beans.factory.annotation.Autowired; 26 | import org.springframework.stereotype.Service; 27 | 28 | @Service 29 | public class ObjectTransform { 30 | 31 | @Autowired 32 | ObjectStore objectCache; 33 | 34 | // Read current value, then mask and or with the desired set 35 | public String maskedValue(PropertyValue value, String val, String result) { 36 | 37 | BigInteger intValue = parse(value, val); 38 | BigInteger resultValue = parse(value, result); 39 | 40 | intValue = intValue.shiftLeft(value.shift()); 41 | BigInteger maskedValue = resultValue.and(value.mask().not()); 42 | maskedValue = maskedValue.or(intValue); 43 | Integer maskedVal = maskedValue.intValue(); 44 | 45 | return String.format("%0" + value.size() + "X", maskedVal); 46 | } 47 | 48 | 49 | private boolean nonNumeric(PropertyValue value) { 50 | if ((value.getType().toLowerCase().equals("f") || value.getType().toLowerCase().equals("float") 51 | || value.getType().toLowerCase().equals("i") 52 | || value.getType().toLowerCase().equals("integer"))) { 53 | return false; 54 | } else { 55 | return true; 56 | } 57 | } 58 | 59 | public String transform(PropertyValue value, String result) { 60 | double floatValue; 61 | 62 | // Do not perform transforms on non-numeric fields 63 | if (nonNumeric(value)) { 64 | return result; 65 | } 66 | 67 | if (value.getLSB() != null) { 68 | BigInteger val = parse(value, result); 69 | 70 | if (!value.mask().equals(BigInteger.ZERO)) { 71 | val = val.and(value.mask()); 72 | } 73 | 74 | if (!value.shift().equals(0)) { 75 | val = val.shiftRight(value.shift()); 76 | } 77 | 78 | if (value.getSigned() && val.bitLength() == (value.size() * 4)) { 79 | BigInteger complement = 80 | new BigInteger(new String(new char[value.size()]).replace("\0", "F"), 16); 81 | val = val.subtract(complement); 82 | } 83 | 84 | if (!objectCache.getTransformData()) { 85 | int intValue = val.intValue(); 86 | return String.valueOf(intValue); 87 | } 88 | 89 | floatValue = val.doubleValue(); 90 | } else { 91 | floatValue = Float.parseFloat(result); 92 | } 93 | 94 | if (!value.base().equals(0)) { 95 | floatValue = Math.pow(value.base(), floatValue); 96 | } 97 | 98 | floatValue = floatValue * value.scale(); 99 | floatValue = floatValue + value.offset(); 100 | 101 | if (value.getType().toLowerCase().equals("f") 102 | || value.getType().toLowerCase().equals("float")) { 103 | return String.valueOf(floatValue); 104 | } 105 | 106 | return String.valueOf(Math.round(floatValue)); 107 | } 108 | 109 | public BigInteger parse(PropertyValue value, String result) { 110 | BigInteger val = BigInteger.ZERO; 111 | 112 | if (result.startsWith("0x")) { 113 | result = result.substring(2, result.length()); 114 | } else { 115 | result = String.format("%0" + value.size() + "X", Integer.decode(result)); 116 | } 117 | 118 | Integer word = value.word() * 2; 119 | 120 | if (word > value.size()) { 121 | word = value.size(); 122 | } 123 | 124 | for (int i = 0; i < result.length() / word; i++) { 125 | Integer start = i * word; 126 | Integer finish = (i + 1) * word; 127 | BigInteger thisword = BigInteger.ZERO; 128 | 129 | for (int j = 0; j < word / 2; j++) { 130 | if (value.LSB()) { 131 | Integer index = finish - j * 2; 132 | Integer intval = Integer.decode("0x" + result.substring(index - 2, index)); 133 | thisword = BigInteger.valueOf(intval).add(thisword.shiftLeft(8)); 134 | } else { 135 | Integer index = start + j * 2; 136 | Integer intval = Integer.decode("0x" + result.substring(index, index + 2)); 137 | thisword = BigInteger.valueOf(intval).add(thisword.shiftLeft(8)); 138 | } 139 | } 140 | 141 | val = thisword.add(val.shiftLeft(word * 4)); 142 | } 143 | 144 | return val; 145 | } 146 | 147 | public String format(PropertyValue value, String arg) { 148 | BigInteger intValue = parse(value, arg); 149 | Integer val = intValue.intValue(); 150 | return String.format("0x%0" + value.size() + "X", val); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/mqtt/DeviceDiscovery.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.mqtt; 20 | 21 | import java.util.Map; 22 | import java.util.regex.Matcher; 23 | import java.util.regex.Pattern; 24 | 25 | import org.edgexfoundry.data.DeviceStore; 26 | import org.edgexfoundry.data.WatcherStore; 27 | import org.edgexfoundry.domain.ScanList; 28 | import org.edgexfoundry.domain.meta.Addressable; 29 | import org.edgexfoundry.domain.meta.AdminState; 30 | import org.edgexfoundry.domain.meta.Device; 31 | import org.edgexfoundry.domain.meta.OperatingState; 32 | import org.edgexfoundry.domain.meta.Protocol; 33 | import org.edgexfoundry.domain.meta.ProvisionWatcher; 34 | import org.edgexfoundry.support.logging.client.EdgeXLogger; 35 | import org.edgexfoundry.support.logging.client.EdgeXLoggerFactory; 36 | import org.springframework.beans.factory.annotation.Autowired; 37 | import org.springframework.stereotype.Service; 38 | 39 | @Service 40 | public class DeviceDiscovery { 41 | private static final EdgeXLogger logger = 42 | EdgeXLoggerFactory.getEdgeXLogger(DeviceDiscovery.class); 43 | 44 | @Autowired 45 | private WatcherStore watchers; 46 | 47 | @Autowired 48 | private DeviceStore devices; 49 | 50 | // TODO Generate protocol dynamically 51 | private Protocol protocol = Protocol.TCP; 52 | 53 | private ProvisionWatcher deviceMatches(Map device) { 54 | for (ProvisionWatcher watcher : watchers.getWatchers()) { 55 | Map identifiers = watcher.getIdentifiers(); 56 | boolean found = true; 57 | 58 | for (Map.Entry entry : identifiers.entrySet()) { 59 | Pattern p = Pattern.compile(entry.getValue()); 60 | Matcher m = null; 61 | String fieldValue = ""; 62 | fieldValue = device.get(entry.getKey()); 63 | m = p.matcher(fieldValue); 64 | 65 | if (m == null) { 66 | logger.error("Identifier field " + entry.getKey() + " was not found."); 67 | break; 68 | } 69 | 70 | if (m.matches()) { 71 | found = found && true; 72 | } else { 73 | found = false; 74 | break; 75 | } 76 | } 77 | 78 | if (found) { 79 | logger.debug("Matching Device " + device + " found."); 80 | return watcher; 81 | } 82 | } 83 | 84 | return null; 85 | } 86 | 87 | private Device deviceExists(Map device) { 88 | return devices.getMetaDevices().stream() 89 | .filter(d -> device.get("address").equals(d.getAddressable().getPath())).findFirst() 90 | .orElse(null); 91 | } 92 | 93 | private Device createDevice(Map device, ProvisionWatcher watcher) { 94 | Device newDevice = new Device(); 95 | newDevice.setProfile(watcher.getProfile()); 96 | newDevice.setService(watcher.getService()); 97 | String name = device.get("name") + " " + device.get("address"); 98 | newDevice.setName(name); 99 | Addressable addressable = 100 | createAddressable(device, name, watcher.getService().getAddressable()); 101 | newDevice.setAddressable(addressable); 102 | newDevice.setLabels(watcher.getService().getLabels()); 103 | newDevice.setAdminState(AdminState.UNLOCKED); 104 | newDevice.setOperatingState(OperatingState.ENABLED); 105 | return newDevice; 106 | } 107 | 108 | private Addressable createAddressable(Map device, String name, 109 | Addressable service) { 110 | Addressable addressable = new Addressable(name, protocol, device.get("interface"), 111 | device.get("address"), service.getPort()); 112 | 113 | return addressable; 114 | } 115 | 116 | public void provision(ScanList availableList) { 117 | if (availableList != null && availableList.getScan().size() > 0) { 118 | for (Map device : availableList.getScan()) { 119 | Device matchingDevice = deviceExists(device); 120 | 121 | if (matchingDevice != null) { 122 | if (matchingDevice.getOperatingState().equals(OperatingState.DISABLED) 123 | || devices.getDevice(matchingDevice.getName()) == null) { 124 | matchingDevice.setOperatingState(OperatingState.ENABLED); 125 | devices.add(matchingDevice); 126 | } 127 | 128 | continue; 129 | } 130 | 131 | ProvisionWatcher watcher = deviceMatches(device); 132 | if (watcher != null) { 133 | // Provision the device 134 | Device newDevice = createDevice(device, watcher); 135 | devices.add(newDevice); 136 | } 137 | } 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/handler/CoreDataMessageHandler.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.handler; 20 | 21 | import java.util.ArrayList; 22 | import java.util.Calendar; 23 | import java.util.List; 24 | import java.util.Map; 25 | 26 | import org.edgexfoundry.controller.DeviceClient; 27 | import org.edgexfoundry.controller.EventClient; 28 | import org.edgexfoundry.data.DeviceStore; 29 | import org.edgexfoundry.domain.MqttObject; 30 | import org.edgexfoundry.domain.ResponseObject; 31 | import org.edgexfoundry.domain.core.Event; 32 | import org.edgexfoundry.domain.core.Reading; 33 | import org.edgexfoundry.domain.meta.Device; 34 | import org.edgexfoundry.domain.meta.OperatingState; 35 | import org.edgexfoundry.support.logging.client.EdgeXLogger; 36 | import org.edgexfoundry.support.logging.client.EdgeXLoggerFactory; 37 | import org.springframework.beans.factory.annotation.Autowired; 38 | import org.springframework.beans.factory.annotation.Value; 39 | import org.springframework.stereotype.Service; 40 | 41 | @Service 42 | public class CoreDataMessageHandler { 43 | 44 | private static final EdgeXLogger logger = 45 | EdgeXLoggerFactory.getEdgeXLogger(CoreDataMessageHandler.class); 46 | 47 | @Value("${service.connect.retries}") 48 | private int retries; 49 | @Value("${service.connect.wait}") 50 | private long delay; 51 | 52 | @Autowired 53 | private DeviceClient deviceClient; 54 | 55 | @Autowired 56 | private EventClient eventClient; 57 | 58 | @Autowired 59 | private DeviceStore devices; 60 | 61 | public Reading buildReading(String key, String value, String deviceName) { 62 | Reading reading = new Reading(); 63 | reading.setName(key); 64 | reading.setValue(value); 65 | reading.setDevice(deviceName); 66 | return reading; 67 | } 68 | 69 | private Event buildEvent(String deviceName, List readings) { 70 | Event event = new Event(deviceName); 71 | event.setReadings(readings); 72 | return event; 73 | } 74 | 75 | private boolean sendEvent(Event event, int attempt) { 76 | if (retries == 0 || attempt < retries) { 77 | if (event != null) { 78 | try { 79 | eventClient.add(event); 80 | return true; 81 | } catch (Exception e) { 82 | // something happened trying to send to 83 | // core data - likely that the service 84 | // is down. 85 | logger.debug("Problem sending event for " + event.getDevice() 86 | + " to core data. Retrying (attempt " + (attempt + 1) + ")..."); 87 | try { 88 | Thread.sleep(delay); 89 | } catch (InterruptedException interrupt) { 90 | logger.debug("Event send delay interrupted"); 91 | interrupt.printStackTrace(); 92 | } 93 | 94 | return sendEvent(event, ++attempt); 95 | } 96 | } 97 | } 98 | return false; 99 | } 100 | 101 | private void updateLastConnected(String deviceName) { 102 | Device device = devices.getDevice(deviceName); 103 | if (device != null) { 104 | deviceClient.updateLastConnected(device.getId(), Calendar.getInstance().getTimeInMillis()); 105 | if (device.getOperatingState().equals(OperatingState.DISABLED)) { 106 | devices.setDeviceByIdOpState(device.getId(), OperatingState.ENABLED); 107 | } 108 | } else { 109 | logger.debug("No device found for device name: " + deviceName 110 | + ". Could not update last connected time"); 111 | } 112 | } 113 | 114 | public List sendCoreData(String deviceName, List readings, 115 | Map objects) { 116 | 117 | try { 118 | 119 | if (objects != null) { 120 | List resps = new ArrayList<>(); 121 | logger.debug("readings: " + readings); 122 | for (Reading reading : readings) { 123 | ResponseObject resp = new ResponseObject(reading.getName(), reading.getValue()); 124 | resps.add(resp); 125 | } 126 | 127 | boolean success = sendEvent(buildEvent(deviceName, readings), 0); 128 | if (success) { 129 | updateLastConnected(deviceName); 130 | return resps; 131 | } else { 132 | if (devices.getDevice(deviceName).getOperatingState().equals(OperatingState.ENABLED)) { 133 | devices.setDeviceOpState(deviceName, OperatingState.DISABLED); 134 | } 135 | 136 | logger.error( 137 | "Could not send event to core data for " + deviceName + ". Check core data service"); 138 | } 139 | } else { 140 | logger.debug( 141 | "No profile object found for the device " + deviceName + ". MQTT message ignored."); 142 | } 143 | } catch (Exception e) { 144 | logger.error("Cannot push the readings to Coredata " + e.getMessage()); 145 | e.printStackTrace(); 146 | } 147 | 148 | return new ArrayList<>(); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/mqtt/messaging/CommandResponseListener.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.mqtt.messaging; 20 | 21 | import javax.annotation.PostConstruct; 22 | import javax.annotation.PreDestroy; 23 | 24 | import org.apache.log4j.Logger; 25 | import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; 26 | import org.eclipse.paho.client.mqttv3.MqttCallback; 27 | import org.eclipse.paho.client.mqttv3.MqttClient; 28 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; 29 | import org.eclipse.paho.client.mqttv3.MqttException; 30 | import org.eclipse.paho.client.mqttv3.MqttMessage; 31 | import org.springframework.beans.factory.annotation.Autowired; 32 | import org.springframework.beans.factory.annotation.Value; 33 | import org.springframework.stereotype.Component; 34 | 35 | @Component 36 | public class CommandResponseListener implements MqttCallback { 37 | 38 | private static final Logger logger = Logger.getLogger(CommandResponseListener.class); 39 | private MqttClient client; 40 | 41 | @Value("${RESPONSE_MQTT_BROKER_PROTO}") 42 | private String cmdrespMqttBrokerProtocol; 43 | @Value("${RESPONSE_MQTT_BROKER}") 44 | private String cmdrespMqttBroker; 45 | @Value("${RESPONSE_MQTT_BROKER_PORT}") 46 | private String cmdrespMqttBrokerPort; 47 | @Value("${RESPONSE_MQTT_CLIENT_ID}") 48 | private String cmdrespMqttClientId; 49 | @Value("${RESPONSE_MQTT_TOPIC}") 50 | private String cmdrespMqttTopic; 51 | @Value("${RESPONSE_MQTT_QOS}") 52 | private int cmdrespMqttQos; 53 | @Value("${RESPONSE_MQTT_USER}") 54 | private String cmdrespMqttUser; 55 | @Value("${RESPONSE_MQTT_PASS}") 56 | private String cmdrespMqttPassword; 57 | @Value("${RESPONSE_MQTT_KEEP_ALIVE}") 58 | private int cmdrespMqttKeepAlive; 59 | 60 | @Autowired 61 | CommandResponseMessageProcessor processor; 62 | 63 | /** 64 | * Called after Spring creates the listener. It starts the listening for MQTT messages off the 65 | * topic. 66 | * 67 | * @throws ClassNotFoundException 68 | */ 69 | @PostConstruct 70 | public void init() throws ClassNotFoundException { 71 | startListening(); 72 | } 73 | 74 | /** 75 | * Called just before the bean is cleaned up on a shutdown by Spring. Specifically, it disconnects 76 | * and closes the MQTT Client. 77 | * 78 | * @throws MqttException 79 | */ 80 | @PreDestroy 81 | public void cleanup() throws MqttException { 82 | client.disconnect(); 83 | client.close(); 84 | } 85 | 86 | /* 87 | * (non-Javadoc) 88 | * 89 | * @see org.eclipse.paho.client.mqttv3.MqttCallback#connectionLost(java.lang. Throwable) 90 | */ 91 | @Override 92 | public void connectionLost(Throwable cause) { 93 | logger.error("Response subscription connection lost:" + cause.getLocalizedMessage()); 94 | // cause.printStackTrace(); 95 | try { 96 | client.close(); 97 | } catch (MqttException e) { 98 | logger.error("Unable to close the client."); 99 | e.printStackTrace(); 100 | } 101 | startListening(); 102 | } 103 | 104 | /* 105 | * (non-Javadoc) 106 | * 107 | * @see org.eclipse.paho.client.mqttv3.MqttCallback#deliveryComplete(org.eclipse. 108 | * paho.client.mqttv3.IMqttDeliveryToken) 109 | */ 110 | @Override 111 | public void deliveryComplete(IMqttDeliveryToken arg0) { 112 | logger.debug("Response message delivery complete."); 113 | } 114 | 115 | /* 116 | * (non-Javadoc) 117 | * 118 | * @see org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived(java.lang. String, 119 | * org.eclipse.paho.client.mqttv3.MqttMessage) 120 | * 121 | */ 122 | @Override 123 | public void messageArrived(String topic, MqttMessage message) throws Exception { 124 | logger.debug("Response message arrived: " + new String(message.getPayload())); 125 | if (cmdrespMqttTopic.equals(topic)) { 126 | processor.process(message.getPayload()); 127 | } 128 | } 129 | 130 | private void startListening() { 131 | logger.debug("Starting listening for response traffic"); 132 | try { 133 | String url = 134 | cmdrespMqttBrokerProtocol + "://" + cmdrespMqttBroker + ":" + cmdrespMqttBrokerPort; 135 | client = new MqttClient(url, cmdrespMqttClientId); 136 | MqttConnectOptions connOpts = new MqttConnectOptions(); 137 | connOpts.setUserName(cmdrespMqttUser); 138 | connOpts.setPassword(cmdrespMqttPassword.toCharArray()); 139 | connOpts.setCleanSession(true); 140 | connOpts.setKeepAliveInterval(cmdrespMqttKeepAlive); 141 | logger.debug("Connecting to response message broker: " + cmdrespMqttBroker); 142 | client.connect(connOpts); 143 | logger.debug("Connected to response message broker"); 144 | client.setCallback(this); 145 | client.subscribe(cmdrespMqttTopic, cmdrespMqttQos); 146 | } catch (MqttException e) { 147 | logger.error("Unable to connect to response message queue. " 148 | + "Unable to respond to command requests."); 149 | e.printStackTrace(); 150 | client = null; 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /docker-files/application.properties: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Copyright 2016-2017 Dell Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # @microservice: device-mqtt 17 | # @author: Jim White, Dell 18 | # @version: 1.0.0 19 | ############################################################################### 20 | #REST read data limit 21 | read.max.limit=100 22 | #logging levels (used to control log4j entries) 23 | logging.level.org.springframework=ERROR 24 | logging.level.org.apache=ERROR 25 | logging.level.org.edgexfoundry=INFO 26 | app.open.msg=This is the device-mqtt micro service 27 | #every 5 minutes (in milliseconds) 28 | heart.beat.time=300000 29 | server.port=49982 30 | #default device service settings 31 | service.name=edgex-device-mqtt 32 | #service.host=localhost 33 | service.host=${service.name} 34 | service.labels=MQTT 35 | service.callback=/api/v1/callback 36 | #connection retry parameters 37 | service.connect.retries=12 38 | service.connect.wait=5000 39 | service.connect.interval=10000 40 | # callback timeout in milliseconds 41 | service.timeout=5000 42 | spring.mvc.dispatch-options-request=true 43 | data.transform=true 44 | mqtt.device.init=Init 45 | mqtt.device.init.args={ value: 1 } 46 | mqtt.device.remove=Remove 47 | mqtt.device.remove.args={ value: 0 } 48 | #----------------------------------- 49 | #Cloud MQTT connection information 50 | #for incoming messages from devices 51 | #Use TestIoTMQTT Broker for testing/dev 52 | INCOMING_MQTT_BROKER_PROTO=tcp 53 | INCOMING_MQTT_BROKER=m11.cloudmqtt.com 54 | INCOMING_MQTT_BROKER_PORT=12439 55 | INCOMING_MQTT_CLIENT_ID=IncomingDataSubscriber 56 | INCOMING_MQTT_TOPIC=DataTopic 57 | INCOMING_MQTT_QOS=0 58 | INCOMING_MQTT_USER=tobeprovided 59 | INCOMING_MQTT_PASS=tobeprovided 60 | #keep alive set to 1 hour 61 | INCOMING_MQTT_KEEP_ALIVE=3600 62 | #for command response messages 63 | RESPONSE_MQTT_BROKER_PROTO=tcp 64 | RESPONSE_MQTT_BROKER=m11.cloudmqtt.com 65 | RESPONSE_MQTT_BROKER_PORT=12439 66 | RESPONSE_MQTT_CLIENT_ID=CommandResponseSubscriber 67 | RESPONSE_MQTT_TOPIC=ResponseTopic 68 | RESPONSE_MQTT_QOS=0 69 | RESPONSE_MQTT_USER=tobeprovided 70 | RESPONSE_MQTT_PASS=tobeprovided 71 | #keep alive set to 1 hour 72 | RESPONSE_MQTT_KEEP_ALIVE=3600 73 | #-----------example test device---------------- 74 | provision.mqtt.device=true 75 | device.profile.name=MQTTTestDeviceProfile.yml 76 | device.name=TestMQTTDevice 77 | device.description=Default Test MQTT Device 78 | device.labels=MQTT, Test 79 | device.addressablename=testMQTTAddressable 80 | #for outgoing test command messages 81 | request.broker.proto=TCP 82 | request.broker=m11.cloudmqtt.com 83 | request.broker.port=12439 84 | request.client.id=OutgoingCommandPublisher 85 | request.topic=CommandTopic 86 | request.user=tobeprovided 87 | request.pass=tobeprovided 88 | #------------------- REST Endpoints --------------------------------------- 89 | # metadata database service connection information 90 | #meta.db.addressable.url=http://localhost:48081/api/v1/addressable 91 | #meta.db.deviceservice.url=http://localhost:48081/api/v1/deviceservice 92 | #meta.db.deviceprofile.url=http://localhost:48081/api/v1/deviceprofile 93 | #meta.db.device.url=http://localhost:48081/api/v1/device 94 | #meta.db.devicemanager.url=http://localhost:48081/api/v1/devicemanager 95 | #meta.db.devicereport.url=http://localhost:48081/api/v1/devicereport 96 | #meta.db.command.url=http://localhost:48081/api/v1/command 97 | #meta.db.event.url=http://localhost:48081/api/v1/scheduleevent 98 | #meta.db.schedule.url=http://localhost:48081/api/v1/schedule 99 | #meta.db.provisionwatcher.url=http://localhost:48081/api/v1/provisionwatcher 100 | #meta.db.ping.url=http://localhost:48081/api/v1/ping 101 | meta.db.addressable.url=http://edgex-core-metadata:48081/api/v1/addressable 102 | meta.db.deviceservice.url=http://edgex-core-metadata:48081/api/v1/deviceservice 103 | meta.db.deviceprofile.url=http://edgex-core-metadata:48081/api/v1/deviceprofile 104 | meta.db.device.url=http://edgex-core-metadata:48081/api/v1/device 105 | meta.db.devicemanager.url=http://edgex-core-metadata:48081/api/v1/devicemanager 106 | meta.db.devicereport.url=http://edgex-core-metadata:48081/api/v1/devicereport 107 | meta.db.command.url=http://edgex-core-metadata:48081/api/v1/command 108 | meta.db.event.url=http://edgex-core-metadata:48081/api/v1/scheduleevent 109 | meta.db.schedule.url=http://edgex-core-metadata:48081/api/v1/schedule 110 | meta.db.provisionwatcher.url=http://edgex-core-metadata:48081/api/v1/provisionwatcher 111 | meta.db.ping.url=http://edgex-core-metadata:48081/api/v1/ping 112 | 113 | #IOT core database service connection information 114 | #core.db.event.url=http://localhost:48080/api/v1/event 115 | #core.db.reading.url=http://localhost:48080/api/v1/reading 116 | #core.db.valuedescriptor.url=http://localhost:48080/api/v1/valuedescriptor 117 | #core.db.ping.url=http://localhost:48080/api/v1/ping 118 | core.db.event.url=http://edgex-core-data:48080/api/v1/event 119 | core.db.reading.url=http://edgex-core-data:48080/api/v1/reading 120 | core.db.valuedescriptor.url=http://edgex-core-data:48080/api/v1/valuedescriptor 121 | core.db.ping.url=http://edgex-core-data:48080/api/v1/ping 122 | logging.remote.url=http://edgex-support-logging:48061/api/v1/logs 123 | 124 | #-----------------Consul Config------------------------------------------ 125 | #The health checking path for Service Registry 126 | spring.cloud.consul.discovery.healthCheckPath=/api/v1/ping 127 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/mqtt/messaging/IncomingListener.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.mqtt.messaging; 20 | 21 | import javax.annotation.PostConstruct; 22 | import javax.annotation.PreDestroy; 23 | 24 | import org.apache.log4j.Logger; 25 | import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; 26 | import org.eclipse.paho.client.mqttv3.MqttCallback; 27 | import org.eclipse.paho.client.mqttv3.MqttClient; 28 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; 29 | import org.eclipse.paho.client.mqttv3.MqttException; 30 | import org.eclipse.paho.client.mqttv3.MqttMessage; 31 | import org.edgexfoundry.Application; 32 | import org.springframework.beans.factory.annotation.Autowired; 33 | import org.springframework.beans.factory.annotation.Value; 34 | import org.springframework.stereotype.Component; 35 | 36 | @Component 37 | public class IncomingListener implements MqttCallback { 38 | 39 | private static final Logger logger = Logger.getLogger(IncomingListener.class); 40 | private MqttClient client; 41 | 42 | @Value("${INCOMING_MQTT_BROKER_PROTO}") 43 | private String incomingMqttBrokerProtocol; 44 | @Value("${INCOMING_MQTT_BROKER}") 45 | private String incomingMqttBroker; 46 | @Value("${INCOMING_MQTT_BROKER_PORT}") 47 | private String incomingMqttBrokerPort; 48 | @Value("${INCOMING_MQTT_CLIENT_ID}") 49 | private String incomingMqttClientId; 50 | @Value("${INCOMING_MQTT_TOPIC}") 51 | private String incomingMqttTopic; 52 | @Value("${INCOMING_MQTT_QOS}") 53 | private int incomingMqttQos; 54 | @Value("${INCOMING_MQTT_USER}") 55 | private String incomingMqttUser; 56 | @Value("${INCOMING_MQTT_PASS}") 57 | private String incomingMqttPassword; 58 | @Value("${INCOMING_MQTT_KEEP_ALIVE}") 59 | private int incomingMqttKeepAlive; 60 | 61 | @Autowired 62 | private MessageProcessor processor; 63 | 64 | /** 65 | * Called after Spring creates the listener. It starts the listening for Mqtt messages off the 66 | * topic. 67 | * 68 | * @throws ClassNotFoundException 69 | */ 70 | @PostConstruct 71 | public void init() throws ClassNotFoundException { 72 | startListening(); 73 | // if incoming message queue client is not available, shut the service 74 | // down (no messages will ever hit the service under the circumstances) 75 | if (client == null) { 76 | Application.setConnected(false); 77 | } 78 | } 79 | 80 | /** 81 | * Called just before the bean is cleaned up on a shutdown by Spring. Specifically, it disconnects 82 | * and closes the Mqtt Client. 83 | * 84 | * @throws MqttException 85 | */ 86 | @PreDestroy 87 | public void cleanup() throws MqttException { 88 | client.disconnect(); 89 | client.close(); 90 | } 91 | 92 | /* 93 | * (non-Javadoc) 94 | * 95 | * @see org.eclipse.paho.client.mqttv3.MqttCallback#connectionLost(java.lang. Throwable) 96 | */ 97 | @Override 98 | public void connectionLost(Throwable cause) { 99 | logger.error("Incoming subscription connection lost:" + cause.getLocalizedMessage()); 100 | // cause.printStackTrace(); 101 | try { 102 | client.close(); 103 | } catch (MqttException e) { 104 | logger.error("Unable to close the client."); 105 | e.printStackTrace(); 106 | } 107 | startListening(); 108 | } 109 | 110 | /* 111 | * (non-Javadoc) 112 | * 113 | * @see org.eclipse.paho.client.mqttv3.MqttCallback#deliveryComplete(org.eclipse. 114 | * paho.client.mqttv3.IMqttDeliveryToken) 115 | */ 116 | @Override 117 | public void deliveryComplete(IMqttDeliveryToken token) { 118 | logger.error("Incoming message delivery complete."); 119 | } 120 | 121 | /* 122 | * (non-Javadoc) 123 | * 124 | * @see org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived(java.lang. String, 125 | * org.eclipse.paho.client.mqttv3.MqttMessage) 126 | * 127 | */ 128 | @Override 129 | public void messageArrived(String topic, MqttMessage message) { 130 | logger.info("Incoming message arrived: " + new String(message.getPayload())); 131 | if (incomingMqttTopic.equals(topic)) { 132 | processor.process(message.getPayload()); 133 | } 134 | } 135 | 136 | private void startListening() { 137 | logger.debug("Starting listening for incoming traffic"); 138 | try { 139 | String url = 140 | incomingMqttBrokerProtocol + "://" + incomingMqttBroker + ":" + incomingMqttBrokerPort; 141 | client = new MqttClient(url, incomingMqttClientId); 142 | MqttConnectOptions connOpts = new MqttConnectOptions(); 143 | connOpts.setUserName(incomingMqttUser); 144 | connOpts.setPassword(incomingMqttPassword.toCharArray()); 145 | connOpts.setCleanSession(true); 146 | connOpts.setKeepAliveInterval(incomingMqttKeepAlive); 147 | logger.debug("Connecting to incoming message broker: " + incomingMqttBroker); 148 | client.connect(connOpts); 149 | logger.debug("Connected to incoming message broker"); 150 | client.setCallback(this); 151 | client.subscribe(incomingMqttTopic, incomingMqttQos); 152 | } catch (MqttException e) { 153 | logger.error("Unable to connect to incoming message queue."); 154 | e.printStackTrace(); 155 | client = null; 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/test/java/org/edgexfoundry/TestMqttDeviceCommandRec.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry; 20 | 21 | import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; 22 | import org.eclipse.paho.client.mqttv3.MqttCallback; 23 | import org.eclipse.paho.client.mqttv3.MqttClient; 24 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; 25 | import org.eclipse.paho.client.mqttv3.MqttException; 26 | import org.eclipse.paho.client.mqttv3.MqttMessage; 27 | import org.edgexfoundry.mqtt.messaging.OutgoingSender; 28 | 29 | import com.google.gson.Gson; 30 | import com.google.gson.JsonElement; 31 | import com.google.gson.JsonObject; 32 | import com.google.gson.JsonParser; 33 | 34 | /* 35 | * Use this class to test receiving commands from the device service and responded back for get 36 | * commands. Use a REST client to send a command to the service like: 37 | * http://localhost:49982/api/v1/devices/{device id}>/message - use POST on this one with 38 | * {"message":"some text"} in body http://localhost:49982/api/v1/devices//ping - use GET 39 | * http://localhost:49982/api/v1/devices//randnum - use GET 40 | * 41 | * If command micro service is running, the same can be performed through command to device service 42 | * like this http://localhost:48082/api/v1/device//command/ 43 | * 44 | * Requires the Device Service, Command, Core Data, Metadata and Mongo to all be running 45 | * 46 | */ 47 | public class TestMqttDeviceCommandRec implements MqttCallback { 48 | 49 | private static String REQ_MQTT_BROKER = "m11.cloudmqtt.com"; 50 | private static String REQ_MQTT_PROTOCOL = "tcp"; 51 | private static int REQ_MQTT_BROKER_PORT = 12439; 52 | private static String REQ_MQTT_CLIENT_ID = "OutgoingCommandSubscriber"; 53 | private static String REQ_MQTT_TOPIC = "CommandTopic"; 54 | private static String REQ_MQTT_USER = "tobeprovided"; 55 | private static String REQ_MQTT_PASS = "tobeprovided"; 56 | 57 | private static String RESP_MQTT_BROKER = "m11.cloudmqtt.com"; 58 | private static String RESP_MQTT_PROTOCOL = "tcp"; 59 | private static int RESP_MQTT_BROKER_PORT = 12439; 60 | private static String RESP_MQTT_CLIENT_ID = "CommandResponsePublisher"; 61 | private static String RESP_MQTT_TOPIC = "ResponseTopic"; 62 | private static String RESP_MQTT_USER = "tobeprovided"; 63 | private static String RESP_MQTT_PASS = "tobeprovided"; 64 | 65 | private static final String CMD_KEY = "cmd"; 66 | 67 | boolean msgRecd = false; 68 | 69 | MqttClient sampleClient; 70 | JsonParser parser = new JsonParser(); 71 | 72 | Gson gson = new Gson(); 73 | 74 | public static void main(String[] args) { 75 | new TestMqttDeviceCommandRec().doDemo(); 76 | } 77 | 78 | public void doDemo() { 79 | try { 80 | String address = REQ_MQTT_PROTOCOL + "://" + REQ_MQTT_BROKER + ":" + REQ_MQTT_BROKER_PORT; 81 | sampleClient = new MqttClient(address, REQ_MQTT_CLIENT_ID); 82 | MqttConnectOptions connOpts = new MqttConnectOptions(); 83 | connOpts.setUserName(REQ_MQTT_USER); 84 | connOpts.setPassword(REQ_MQTT_PASS.toCharArray()); 85 | connOpts.setCleanSession(true); 86 | connOpts.setKeepAliveInterval(30); 87 | System.out.println("Connecting to broker"); 88 | sampleClient.connect(connOpts); 89 | System.out.println("Connected"); 90 | sampleClient.setCallback(this); 91 | sampleClient.subscribe(REQ_MQTT_TOPIC, 0); 92 | while (true) { 93 | try { 94 | Thread.sleep(1000); 95 | } catch (InterruptedException e) { 96 | e.printStackTrace(); 97 | } 98 | } 99 | } catch (MqttException e) { 100 | e.printStackTrace(); 101 | } 102 | } 103 | 104 | @Override 105 | public void connectionLost(Throwable cause) { 106 | System.out.println("oops connection lost + " + cause.getMessage()); 107 | } 108 | 109 | @Override 110 | public void messageArrived(String topic, MqttMessage message) throws Exception { 111 | String payload = new String(message.getPayload()); 112 | System.out.println("Red'c command: " + payload); 113 | JsonObject jsonObject = parser.parse(payload).getAsJsonObject(); 114 | String cmd = extractCommandData(jsonObject, CMD_KEY); 115 | switch (cmd) { 116 | case "ping": 117 | sendResponse(pingResponse(jsonObject)); 118 | break; 119 | case "randnum": 120 | sendResponse(randResponse(jsonObject)); 121 | break; 122 | default: 123 | sendResponse(payload); 124 | } 125 | msgRecd = true; 126 | } 127 | 128 | @Override 129 | public void deliveryComplete(IMqttDeliveryToken token) { 130 | System.out.println("delivery complete"); 131 | } 132 | 133 | private String pingResponse(JsonObject j) { 134 | j.addProperty("ping", "pong"); 135 | return j.toString(); 136 | } 137 | 138 | private String randResponse(JsonObject j) { 139 | j.addProperty("randnum", "42.0"); 140 | return j.toString(); 141 | } 142 | 143 | private void sendResponse(String returnPayload) { 144 | byte[] msg = returnPayload.getBytes(); 145 | System.out.println("Sending response message"); 146 | String address = RESP_MQTT_PROTOCOL + "://" + RESP_MQTT_BROKER + ":" + RESP_MQTT_BROKER_PORT; 147 | OutgoingSender sender = new OutgoingSender(address, RESP_MQTT_CLIENT_ID, RESP_MQTT_USER, 148 | RESP_MQTT_PASS, RESP_MQTT_TOPIC); 149 | sender.sendMessage(msg); 150 | System.out.println("sent: " + returnPayload); 151 | sender.closeClient(); 152 | } 153 | 154 | private String extractCommandData(JsonObject jsonObject, String dataPart) { 155 | JsonElement element = jsonObject.get(dataPart); 156 | if (element != null) { 157 | return element.getAsString(); 158 | } else { 159 | return null; 160 | } 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/controller/UpdateController.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.controller; 20 | 21 | import javax.servlet.http.HttpServletRequest; 22 | 23 | import org.edgexfoundry.domain.meta.ActionType; 24 | import org.edgexfoundry.domain.meta.CallbackAlert; 25 | import org.edgexfoundry.exception.controller.ClientException; 26 | import org.edgexfoundry.exception.controller.NotFoundException; 27 | import org.edgexfoundry.handler.UpdateHandler; 28 | import org.edgexfoundry.support.logging.client.EdgeXLogger; 29 | import org.edgexfoundry.support.logging.client.EdgeXLoggerFactory; 30 | import org.springframework.beans.factory.annotation.Autowired; 31 | import org.springframework.web.bind.annotation.RequestBody; 32 | import org.springframework.web.bind.annotation.RequestMapping; 33 | import org.springframework.web.bind.annotation.RestController; 34 | 35 | @RestController 36 | public class UpdateController { 37 | 38 | private static final EdgeXLogger logger = 39 | EdgeXLoggerFactory.getEdgeXLogger(UpdateController.class); 40 | 41 | @Autowired 42 | UpdateHandler update; 43 | 44 | 45 | @RequestMapping("/${service.callback}") 46 | public void getCallback(HttpServletRequest request, 47 | @RequestBody(required = false) CallbackAlert data) { 48 | 49 | ActionType actionType = data.getType(); 50 | String id = data.getId(); 51 | String method = request.getMethod(); 52 | 53 | // TODO: simply this logic using switch statements 54 | if (actionType == null || id == null || method == null) { 55 | throw new ClientException("Callback parameters were null"); 56 | } 57 | 58 | if (ActionType.DEVICE.equals(actionType) && method.equals("POST")) { 59 | addDevice(id); 60 | } 61 | 62 | if (ActionType.DEVICE.equals(actionType) && method.equals("PUT")) { 63 | updateDevice(id); 64 | } 65 | 66 | if (ActionType.DEVICE.equals(actionType) && method.equals("DELETE")) { 67 | deleteDevice(id); 68 | } 69 | 70 | if (ActionType.PROFILE.equals(actionType) && method.equals("PUT")) { 71 | updateProfile(id); 72 | } 73 | 74 | if (ActionType.PROVISIONWATCHER.equals(actionType) && method.equals("POST")) { 75 | addWatcher(id); 76 | } 77 | 78 | if (ActionType.PROVISIONWATCHER.equals(actionType) && method.equals("PUT")) { 79 | updateWatcher(id); 80 | } 81 | 82 | if (ActionType.PROVISIONWATCHER.equals(actionType) && method.equals("DELETE")) { 83 | deleteWatcher(id); 84 | } 85 | 86 | } 87 | 88 | public void addWatcher(@RequestBody String provisionWatcher) { 89 | if (provisionWatcher != null) { 90 | if (update.addWatcher(provisionWatcher)) { 91 | logger.debug("New device watcher received to add devices with provision watcher id:" 92 | + provisionWatcher); 93 | } else { 94 | logger.error("Received add device provision watcher request without an id attached."); 95 | throw new NotFoundException("provisionWatcher", provisionWatcher); 96 | } 97 | } 98 | } 99 | 100 | public void updateWatcher(@RequestBody String provisionWatcher) { 101 | if (provisionWatcher != null) { 102 | if (update.updateWatcher(provisionWatcher)) { 103 | logger.debug("Update device provision watcher with id:" + provisionWatcher); 104 | } else { 105 | logger.error("Received update device provision watcher request without an id attached."); 106 | throw new NotFoundException("provisionWatcher", provisionWatcher); 107 | } 108 | } 109 | } 110 | 111 | public void deleteWatcher(@RequestBody String provisionWatcher) { 112 | if (provisionWatcher != null) { 113 | if (update.removeWatcher(provisionWatcher)) { 114 | logger.debug("Remove device provision watcher with id:" + provisionWatcher); 115 | } else { 116 | logger.error("Received remove device provision watcher request without an id attached."); 117 | throw new NotFoundException("provisionWatcher", provisionWatcher); 118 | } 119 | } 120 | } 121 | 122 | public void addDevice(@RequestBody String deviceId) { 123 | if (deviceId != null) { 124 | if (update.addDevice(deviceId)) { 125 | logger.debug("Added device. Received add device request with id:" + deviceId); 126 | } else { 127 | logger.error("Received add device request without a device id attached."); 128 | throw new NotFoundException("device", deviceId); 129 | } 130 | } 131 | } 132 | 133 | public void updateDevice(@RequestBody String deviceId) { 134 | if (deviceId != null) { 135 | if (update.updateDevice(deviceId)) { 136 | logger.debug("Updated device. Received update device request with id:" + deviceId); 137 | } else { 138 | logger.error("Received update device request without a device id attached."); 139 | throw new NotFoundException("device", deviceId); 140 | } 141 | } 142 | } 143 | 144 | public void deleteDevice(@RequestBody String deviceId) { 145 | if (deviceId != null) { 146 | if (update.deleteDevice(deviceId)) { 147 | logger.debug("Removing device. Received delete device request with id:" + deviceId); 148 | } else { 149 | logger.error("Received delete device request without a device id attached."); 150 | throw new NotFoundException("device", deviceId); 151 | } 152 | } 153 | } 154 | 155 | public void updateProfile(@RequestBody String profileId) { 156 | if (profileId != null) { 157 | if (update.updateProfile(profileId)) { 158 | logger.debug("Updated profile. Received update profile request with id:" + profileId); 159 | } else { 160 | logger.error("Received update profile request without a profile id attached."); 161 | throw new NotFoundException("profile", profileId); 162 | } 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/data/WatcherStore.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.data; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | import java.util.stream.Collectors; 24 | 25 | import org.edgexfoundry.controller.DeviceProfileClient; 26 | import org.edgexfoundry.controller.DeviceServiceClient; 27 | import org.edgexfoundry.controller.ProvisionWatcherClient; 28 | import org.edgexfoundry.domain.SimpleWatcher; 29 | import org.edgexfoundry.domain.meta.DeviceProfile; 30 | import org.edgexfoundry.domain.meta.DeviceService; 31 | import org.edgexfoundry.domain.meta.ProvisionWatcher; 32 | import org.edgexfoundry.support.logging.client.EdgeXLogger; 33 | import org.edgexfoundry.support.logging.client.EdgeXLoggerFactory; 34 | import org.springframework.beans.factory.annotation.Autowired; 35 | import org.springframework.stereotype.Repository; 36 | 37 | @Repository 38 | public class WatcherStore { 39 | 40 | private static final EdgeXLogger logger = EdgeXLoggerFactory.getEdgeXLogger(WatcherStore.class); 41 | 42 | @Autowired 43 | private ProvisionWatcherClient provisionClient; 44 | 45 | @Autowired 46 | private SimpleWatcher defaultWatchers; 47 | 48 | @Autowired 49 | private DeviceProfileClient profileClient; 50 | 51 | @Autowired 52 | private DeviceServiceClient serviceClient; 53 | 54 | private List watchers = new ArrayList(); 55 | 56 | public List getWatchers() { 57 | return watchers; 58 | } 59 | 60 | public void initialize(String deviceServiceId) { 61 | List metaWatchers = 62 | provisionClient.provisionWatcherForService(deviceServiceId); 63 | 64 | for (ProvisionWatcher watcher : metaWatchers) { 65 | add(watcher); 66 | } 67 | 68 | addDefaultWatchers(deviceServiceId); 69 | } 70 | 71 | public boolean add(String provisionWatcher) { 72 | ProvisionWatcher watcher = provisionClient.provisionWatcher(provisionWatcher); 73 | return add(watcher); 74 | } 75 | 76 | public boolean add(ProvisionWatcher watcher) { 77 | if (watchers.stream().noneMatch(w -> w.getName().equals(watcher.getName()))) { 78 | if (watcher.getProfile().getId() == null) { 79 | logger.info("Profile " + watcher.getProfile().getName() 80 | + " has not been added to metadata, watcher will not be added"); 81 | return false; 82 | } 83 | 84 | if (watcher.getId() == null) { 85 | try { 86 | watcher.setId(provisionClient.add(watcher)); 87 | } catch (Exception e) { 88 | logger.error("Error adding new provision watcher " + watcher.getName() + " error is: " 89 | + e.getMessage()); 90 | } 91 | } 92 | 93 | watchers.add(watcher); 94 | } 95 | 96 | return true; 97 | } 98 | 99 | public boolean remove(String provisionWatcher) { 100 | ProvisionWatcher watcher; 101 | 102 | try { 103 | watcher = provisionClient.provisionWatcherForName(provisionWatcher); 104 | } catch (Exception e) { 105 | watcher = new ProvisionWatcher(); 106 | watcher.setId(provisionWatcher); 107 | } 108 | 109 | return remove(watcher); 110 | } 111 | 112 | public boolean remove(ProvisionWatcher provisionWatcher) { 113 | ProvisionWatcher watcher = watchers.stream() 114 | .filter(w -> w.getId().equals(provisionWatcher.getId())).findAny().orElse(null); 115 | 116 | if (watcher != null) { 117 | watchers.remove(watcher); 118 | } 119 | 120 | return true; 121 | } 122 | 123 | public boolean update(String provisionWatcher) { 124 | ProvisionWatcher watcher = provisionClient.provisionWatcherForName(provisionWatcher); 125 | return update(watcher); 126 | } 127 | 128 | public boolean update(ProvisionWatcher provisionWatcher) { 129 | ProvisionWatcher watcher = watchers.stream() 130 | .filter(w -> w.getId().equals(provisionWatcher.getId())).findAny().orElse(null); 131 | 132 | if (watcher != null) { 133 | watchers.remove(watcher); 134 | return add(provisionWatcher); 135 | } 136 | 137 | return false; 138 | } 139 | 140 | public DeviceProfile getWatcherProfile(ProvisionWatcher watcher) { 141 | DeviceProfile profile = profileClient.deviceProfileForName(watcher.getProfile().getName()); 142 | return profile; 143 | } 144 | 145 | public Integer addDefaultWatchers(String deviceServiceId) { 146 | for (int i = 0; i < defaultWatchers.getSize(); i++) { 147 | ProvisionWatcher watcher = new ProvisionWatcher(); 148 | watcher.setName(defaultWatchers.getName()[i]); 149 | try { 150 | ProvisionWatcher existing = provisionClient.provisionWatcherForName(watcher.getName()); 151 | add(existing); 152 | } catch (Exception notfound) { 153 | watcher.setIdentifiers(defaultWatchers.getIdentifiers().get(i)); 154 | DeviceService service = serviceClient.deviceService(deviceServiceId); 155 | 156 | if (service.getAddressable().getName().equals(defaultWatchers.getService()[i])) { 157 | watcher.setService(service); 158 | } 159 | 160 | DeviceProfile profile; 161 | try { 162 | profile = profileClient.deviceProfileForName(defaultWatchers.getProfile()[i]); 163 | } catch (Exception e) { 164 | profile = new DeviceProfile(); 165 | profile.setName(defaultWatchers.getProfile()[i]); 166 | } 167 | 168 | watcher.setProfile(profile); 169 | add(watcher); 170 | } 171 | } 172 | 173 | return watchers.size(); 174 | } 175 | 176 | public boolean updateProfile(String profileId) { 177 | DeviceProfile profile; 178 | try { 179 | profile = profileClient.deviceProfile(profileId); 180 | } catch (Exception e) { 181 | // No such profile exists to update 182 | return true; 183 | } 184 | 185 | boolean success = true; 186 | for (ProvisionWatcher watcher : watchers.stream() 187 | .filter(w -> profile.getName().equals(w.getProfile().getName())) 188 | .collect(Collectors.toList())) { 189 | 190 | // update all devices that use the profile 191 | watcher.setProfile(profile); 192 | success &= update(watcher); 193 | } 194 | return success; 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 12 | 4.0.0 13 | org.edgexfoundry 14 | device-mqtt 15 | 0.5.0-SNAPSHOT 16 | EdgeX Foundry MQTT Micro Service 17 | EdgeX Foundry MQTT Device Service - initially created by the EdgeX Foundry DS SDK 18 | 19 | 20 | 2.5.5 21 | 3.0 22 | 3.0.19.Final 23 | 1.1.0 24 | 0.5.0-SNAPSHOT 25 | 0.5.0-SNAPSHOT 26 | 0.5.0-SNAPSHOT 27 | 0.5.0-SNAPSHOT 28 | 0.5.0-SNAPSHOT 29 | 0.5.0-SNAPSHOT 30 | Brixton.SR5 31 | 1.8 32 | https://nexus.edgexfoundry.org 33 | content/repositories 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-parent 39 | 1.3.7.RELEASE 40 | 41 | 42 | 43 | 44 | snapshots 45 | EdgeX Snapshot Repository 46 | ${nexusproxy}/${repobasepath}/snapshots 47 | 48 | 49 | 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-starter-web 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-starter-logging 62 | 63 | 64 | 65 | 66 | org.springframework.boot 67 | spring-boot-starter-log4j 68 | 69 | 70 | org.springframework 71 | spring-context-support 72 | 73 | 74 | org.springframework 75 | spring-test 76 | 77 | 78 | org.springframework.boot 79 | spring-boot 80 | 81 | 82 | org.springframework.boot 83 | spring-boot-autoconfigure 84 | 85 | 86 | org.springframework 87 | spring-context 88 | 89 | 90 | log4j 91 | log4j 92 | 93 | 94 | org.springframework.cloud 95 | spring-cloud-starter-consul-all 96 | 97 | 98 | javax.ws.rs 99 | jsr311-api 100 | 101 | 102 | com.google.code.findbugs 103 | jsr305 104 | 105 | 106 | 107 | 108 | com.google.code.gson 109 | gson 110 | 111 | 112 | org.jboss.resteasy 113 | resteasy-client 114 | ${jboss-resteasy.version} 115 | 116 | 117 | org.apache.commons 118 | commons-lang3 119 | ${apache-commons.version} 120 | 121 | 122 | org.yaml 123 | snakeyaml 124 | 125 | 126 | org.eclipse.paho 127 | org.eclipse.paho.client.mqttv3 128 | ${paho.version} 129 | 130 | 131 | org.edgexfoundry 132 | core-metadata-client 133 | ${core-metadata-client.version} 134 | 135 | 136 | org.edgexfoundry 137 | core-domain 138 | ${core-domain.version} 139 | 140 | 141 | org.edgexfoundry 142 | core-data-client 143 | ${core-data-client.version} 144 | 145 | 146 | org.edgexfoundry 147 | core-exception 148 | ${core-exception.version} 149 | 150 | 151 | org.edgexfoundry 152 | core-test 153 | ${core-test.version} 154 | 155 | 156 | org.edgexfoundry 157 | support-logging-client 158 | ${support-logging-client.version} 159 | 160 | 161 | junit 162 | junit 163 | test 164 | 165 | 166 | 167 | 168 | 169 | 170 | org.springframework.cloud 171 | spring-cloud-dependencies 172 | ${spring.cloud.version} 173 | pom 174 | import 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | org.springframework.boot 183 | spring-boot-maven-plugin 184 | 185 | 186 | org.apache.maven.plugins 187 | maven-compiler-plugin 188 | 189 | ${java.version} 190 | ${java.version} 191 | 192 | 193 | 194 | 195 | 196 | 197 | snapshots 198 | EdgeX Snapshots Repository 199 | ${nexusproxy}/${repobasepath}/snapshots 200 | 201 | 202 | staging 203 | EdgeX Staging Repository 204 | ${nexusproxy}/${repobasepath}/staging 205 | 206 | 207 | 208 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/data/ObjectStore.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.data; 20 | 21 | import java.util.ArrayList; 22 | import java.util.HashMap; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.stream.Collectors; 26 | 27 | import org.edgexfoundry.domain.MqttObject; 28 | import org.edgexfoundry.domain.core.Reading; 29 | import org.edgexfoundry.domain.meta.Device; 30 | import org.edgexfoundry.domain.meta.OperatingState; 31 | import org.edgexfoundry.domain.meta.PropertyValue; 32 | import org.edgexfoundry.domain.meta.ResourceOperation; 33 | import org.edgexfoundry.exception.controller.NotFoundException; 34 | import org.edgexfoundry.handler.CoreDataMessageHandler; 35 | import org.edgexfoundry.mqtt.ObjectTransform; 36 | import org.springframework.beans.factory.annotation.Autowired; 37 | import org.springframework.beans.factory.annotation.Value; 38 | import org.springframework.stereotype.Repository; 39 | 40 | import com.google.gson.JsonObject; 41 | 42 | @Repository 43 | public class ObjectStore { 44 | 45 | @Value("${data.transform:true}") 46 | private Boolean transformData; 47 | 48 | @Autowired 49 | private ProfileStore profiles; 50 | 51 | @Autowired 52 | private ObjectTransform transform; 53 | 54 | @Autowired 55 | private CoreDataMessageHandler processor; 56 | 57 | @Value("${data.cache.size:1}") 58 | private int cacheSize; 59 | 60 | private Map>> objectCache = new HashMap<>(); 61 | 62 | private Map>> responseCache = new HashMap<>(); 63 | 64 | public Boolean getTransformData() { 65 | return transformData; 66 | } 67 | 68 | public void setTransformData(Boolean transform) { 69 | transformData = transform; 70 | } 71 | 72 | public void put(Device device, ResourceOperation operation, String value) { 73 | if (value == null || value.equals("") || value.equals("{}")) { 74 | return; 75 | } 76 | 77 | List objectsList = createObjectsList(operation, device); 78 | String deviceId = device.getId(); 79 | List readings = new ArrayList<>(); 80 | 81 | for (MqttObject obj : objectsList) { 82 | String objectName = obj.getName(); 83 | String result = transformResult(value, obj, device, operation); 84 | 85 | Reading reading = processor.buildReading(objectName, result, device.getName()); 86 | readings.add(reading); 87 | 88 | synchronized (objectCache) { 89 | if (objectCache.get(deviceId) == null) { 90 | objectCache.put(deviceId, new HashMap>()); 91 | } 92 | 93 | if (objectCache.get(deviceId).get(objectName) == null) { 94 | objectCache.get(deviceId).put(objectName, new ArrayList()); 95 | } 96 | 97 | objectCache.get(deviceId).get(objectName).add(0, result); 98 | 99 | if (objectCache.get(deviceId).get(objectName).size() == cacheSize) { 100 | objectCache.get(deviceId).get(objectName).remove(cacheSize - 1); 101 | } 102 | } 103 | } 104 | 105 | String operationId = 106 | objectsList.stream().map(o -> o.getName()).collect(Collectors.toList()).toString(); 107 | 108 | synchronized (responseCache) { 109 | if (responseCache.get(deviceId) == null) { 110 | responseCache.put(deviceId, new HashMap>()); 111 | } 112 | 113 | responseCache.get(deviceId).put(operationId, readings); 114 | } 115 | } 116 | 117 | private List createObjectsList(ResourceOperation operation, Device device) { 118 | Map objects = profiles.getObjects().get(device.getName()); 119 | List objectsList = new ArrayList<>(); 120 | 121 | if (operation != null && objects != null) { 122 | MqttObject object = objects.get(operation.getObject()); 123 | 124 | if (profiles.descriptorExists(operation.getParameter())) { 125 | object.setName(operation.getParameter()); 126 | objectsList.add(object); 127 | } else if (profiles.descriptorExists(object.getName())) { 128 | objectsList.add(object); 129 | } 130 | 131 | if (operation.getSecondary() != null) { 132 | for (String secondary : operation.getSecondary()) { 133 | if (profiles.descriptorExists(secondary)) { 134 | objectsList.add(objects.get(secondary)); 135 | } 136 | } 137 | } 138 | } 139 | 140 | return objectsList; 141 | } 142 | 143 | private String transformResult(String result, MqttObject object, Device device, 144 | ResourceOperation operation) { 145 | PropertyValue propValue = object.getProperties().getValue(); 146 | 147 | String transformResult = transform.transform(propValue, result); 148 | 149 | // if there is an assertion set for the object on a get command, test it 150 | // if it fails the assertion, pass error to core services (disable device?) 151 | if (propValue.getAssertion() != null) { 152 | if (!transformResult.equals(propValue.getAssertion().toString())) { 153 | device.setOperatingState(OperatingState.DISABLED); 154 | return "Assertion failed with value: " + transformResult; 155 | } 156 | } 157 | 158 | Map mappings = operation.getMappings(); 159 | 160 | if (mappings != null && mappings.containsKey(transformResult)) { 161 | transformResult = mappings.get(transformResult); 162 | } 163 | 164 | return transformResult; 165 | } 166 | 167 | public String get(String deviceId, String object) { 168 | return get(deviceId, object, 1).get(0); 169 | } 170 | 171 | private List get(String deviceId, String object, int i) { 172 | if (objectCache.get(deviceId) == null || objectCache.get(deviceId).get(object) == null 173 | || objectCache.get(deviceId).get(object).size() < i) { 174 | return null; 175 | } 176 | 177 | return objectCache.get(deviceId).get(object).subList(0, i); 178 | } 179 | 180 | public JsonObject get(Device device, ResourceOperation operation) { 181 | JsonObject jsonObject = new JsonObject(); 182 | List objectsList = createObjectsList(operation, device); 183 | 184 | for (MqttObject obj : objectsList) { 185 | String objectName = obj.getName(); 186 | jsonObject.addProperty(objectName, get(device.getId(), objectName)); 187 | } 188 | 189 | return jsonObject; 190 | } 191 | 192 | public List getResponses(Device device, ResourceOperation operation) { 193 | String deviceId = device.getId(); 194 | List objectsList = createObjectsList(operation, device); 195 | 196 | if (objectsList == null) { 197 | throw new NotFoundException("device", deviceId); 198 | } 199 | 200 | String operationId = 201 | objectsList.stream().map(o -> o.getName()).collect(Collectors.toList()).toString(); 202 | 203 | if (responseCache.get(deviceId) == null 204 | || responseCache.get(deviceId).get(operationId) == null) { 205 | return new ArrayList<>(); 206 | } 207 | 208 | return responseCache.get(deviceId).get(operationId); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/data/ProfileStore.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.data; 20 | 21 | import java.util.ArrayList; 22 | import java.util.HashMap; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.stream.Collectors; 26 | 27 | import org.edgexfoundry.controller.DeviceProfileClient; 28 | import org.edgexfoundry.controller.ValueDescriptorClient; 29 | import org.edgexfoundry.domain.MqttObject; 30 | import org.edgexfoundry.domain.common.IoTType; 31 | import org.edgexfoundry.domain.common.ValueDescriptor; 32 | import org.edgexfoundry.domain.meta.Command; 33 | import org.edgexfoundry.domain.meta.Device; 34 | import org.edgexfoundry.domain.meta.DeviceObject; 35 | import org.edgexfoundry.domain.meta.DeviceProfile; 36 | import org.edgexfoundry.domain.meta.ProfileResource; 37 | import org.edgexfoundry.domain.meta.PropertyValue; 38 | import org.edgexfoundry.domain.meta.ResourceOperation; 39 | import org.edgexfoundry.domain.meta.Units; 40 | import org.edgexfoundry.support.logging.client.EdgeXLogger; 41 | import org.edgexfoundry.support.logging.client.EdgeXLoggerFactory; 42 | import org.springframework.beans.factory.annotation.Autowired; 43 | import org.springframework.stereotype.Repository; 44 | 45 | @Repository 46 | public class ProfileStore { 47 | private static final EdgeXLogger logger = EdgeXLoggerFactory.getEdgeXLogger(ProfileStore.class); 48 | 49 | @Autowired 50 | private ValueDescriptorClient valueDescriptorClient; 51 | 52 | @Autowired 53 | private DeviceProfileClient deviceProfileClient; 54 | 55 | private List valueDescriptors = new ArrayList<>(); 56 | 57 | // map (key of device name) to cache of each devices resources keyed by resource name 58 | // mapped to resource operations arrays keyed by get or put operation 59 | private Map>>> commands = new HashMap<>(); 60 | 61 | public Map>>> getCommands() { 62 | return commands; 63 | } 64 | 65 | // map (key of device name) to cache each devices profile objects by 66 | // profile 67 | // object key 68 | private Map> objects = new HashMap<>(); 69 | 70 | public Map> getObjects() { 71 | return objects; 72 | } 73 | 74 | public void updateDevice(Device device) { 75 | removeDevice(device); 76 | addDevice(device); 77 | } 78 | 79 | public void removeDevice(Device device) { 80 | objects.remove(device.getName()); 81 | commands.remove(device.getName()); 82 | } 83 | 84 | public void addDevice(Device device) { 85 | 86 | // put the device's profile resources in the commands map 87 | Map>> deviceOperations = new HashMap<>(); 88 | List descriptors; 89 | try { 90 | descriptors = valueDescriptorClient.valueDescriptors(); 91 | } catch (Exception e) { 92 | descriptors = new ArrayList<>(); 93 | } 94 | 95 | List ops = new ArrayList<>(); 96 | 97 | // If profile is not complete, update it 98 | if (device.getProfile().getDeviceResources() == null) { 99 | DeviceProfile profile = 100 | deviceProfileClient.deviceProfileForName(device.getProfile().getName()); 101 | device.setProfile(profile); 102 | addDevice(device); 103 | return; 104 | } 105 | 106 | List usedDescriptors = new ArrayList<>(); 107 | for (Command command : device.getProfile().getCommands()) { 108 | usedDescriptors.addAll(command.associatedValueDescriptors()); 109 | } 110 | 111 | for (ProfileResource resource : device.getProfile().getResources()) { 112 | Map> operations = 113 | new HashMap>(); 114 | operations.put("get", resource.getGet()); 115 | operations.put("set", resource.getSet()); 116 | deviceOperations.put(resource.getName().toLowerCase(), operations); 117 | 118 | if (resource.getGet() != null) { 119 | ops.addAll(resource.getGet()); 120 | } 121 | 122 | if (resource.getSet() != null) { 123 | ops.addAll(resource.getSet()); 124 | } 125 | } 126 | 127 | // put the device's profile objects in the objects map 128 | // put the device's profile objects in the commands map if no resource exists 129 | Map deviceObjects = new HashMap<>(); 130 | for (DeviceObject object : device.getProfile().getDeviceResources()) { 131 | MqttObject mqttObject = new MqttObject(object); 132 | 133 | PropertyValue value = object.getProperties().getValue(); 134 | 135 | deviceObjects.put(object.getName(), mqttObject); 136 | 137 | // if there is no resource defined for an object, create one based on the 138 | // RW parameters 139 | if (!deviceOperations.containsKey(object.getName().toLowerCase())) { 140 | String readWrite = value.getReadWrite(); 141 | 142 | Map> operations = 143 | new HashMap>(); 144 | 145 | if (readWrite.toLowerCase().contains("r")) { 146 | ResourceOperation resource = new ResourceOperation("get", object.getName()); 147 | List getOp = new ArrayList<>(); 148 | getOp.add(resource); 149 | operations.put(resource.getOperation().toLowerCase(), getOp); 150 | ops.add(resource); 151 | } 152 | 153 | if (readWrite.toLowerCase().contains("w")) { 154 | ResourceOperation resource = new ResourceOperation("set", object.getName()); 155 | List setOp = new ArrayList<>(); 156 | setOp.add(resource); 157 | operations.put(resource.getOperation().toLowerCase(), setOp); 158 | ops.add(resource); 159 | } 160 | 161 | deviceOperations.put(object.getName().toLowerCase(), operations); 162 | } 163 | } 164 | 165 | objects.put(device.getName(), deviceObjects); 166 | commands.put(device.getName(), deviceOperations); 167 | 168 | // Create a value descriptor for each parameter using its underlying object 169 | for (ResourceOperation op : ops) { 170 | ValueDescriptor descriptor = descriptors.stream() 171 | .filter(d -> d.getName().equals(op.getParameter())).findAny().orElse(null); 172 | 173 | if (descriptor == null) { 174 | if (!usedDescriptors.contains(op.getParameter())) { 175 | continue; 176 | } 177 | 178 | DeviceObject object = device.getProfile().getDeviceResources().stream() 179 | .filter(obj -> obj.getName().equals(op.getObject())).findAny().orElse(null); 180 | 181 | descriptor = createDescriptor(op.getParameter(), object, device); 182 | } 183 | 184 | valueDescriptors.add(descriptor); 185 | descriptors.add(descriptor); 186 | } 187 | } 188 | 189 | private ValueDescriptor createDescriptor(String name, DeviceObject object, Device device) { 190 | PropertyValue value = object.getProperties().getValue(); 191 | Units units = object.getProperties().getUnits(); 192 | ValueDescriptor descriptor = new ValueDescriptor(name, value.getMinimum(), value.getMaximum(), 193 | IoTType.valueOf(value.getType().substring(0, 1)), units.getDefaultValue(), 194 | value.getDefaultValue(), "%s", null, object.getDescription()); 195 | 196 | try { 197 | descriptor.setId(valueDescriptorClient.add(descriptor)); 198 | } catch (Exception e) { 199 | logger.error("Adding Value descriptor: " + descriptor.getName() + " failed with error " 200 | + e.getMessage()); 201 | } 202 | 203 | return descriptor; 204 | } 205 | 206 | public List getValueDescriptors() { 207 | return valueDescriptors; 208 | } 209 | 210 | public boolean descriptorExists(String name) { 211 | return !getValueDescriptors().stream().filter(desc -> desc.getName().equals(name)) 212 | .collect(Collectors.toList()).isEmpty(); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/BaseService.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry; 20 | 21 | import javax.annotation.PostConstruct; 22 | import javax.ws.rs.NotFoundException; 23 | 24 | import org.edgexfoundry.controller.AddressableClient; 25 | import org.edgexfoundry.controller.DeviceServiceClient; 26 | import org.edgexfoundry.domain.meta.Addressable; 27 | import org.edgexfoundry.domain.meta.AdminState; 28 | import org.edgexfoundry.domain.meta.DeviceService; 29 | import org.edgexfoundry.domain.meta.OperatingState; 30 | import org.edgexfoundry.domain.meta.Protocol; 31 | import org.edgexfoundry.support.logging.client.EdgeXLogger; 32 | import org.edgexfoundry.support.logging.client.EdgeXLoggerFactory; 33 | import org.springframework.beans.factory.annotation.Autowired; 34 | import org.springframework.beans.factory.annotation.Value; 35 | import org.springframework.context.annotation.ImportResource; 36 | import org.springframework.scheduling.annotation.Async; 37 | 38 | @ImportResource("spring-config.xml") 39 | public class BaseService { 40 | 41 | private static final EdgeXLogger logger = EdgeXLoggerFactory.getEdgeXLogger(BaseService.class); 42 | 43 | // service name 44 | @Value("${service.name}") 45 | private String serviceName; 46 | 47 | // service Address Info 48 | @Value("${service.host}") 49 | private String host; 50 | 51 | @Value("${server.port}") 52 | private int port; 53 | 54 | // service labels 55 | @Value("${service.labels}") 56 | private String[] labels; 57 | 58 | // service callback root 59 | @Value("${service.callback}") 60 | private String callbackUrl; 61 | 62 | // TODO: This should become a service domain object , not a device service domain object 63 | private DeviceService service; 64 | 65 | // TODO: This should become a BaseServiceClient 66 | @Autowired 67 | private DeviceServiceClient deviceServiceClient; 68 | 69 | @Autowired 70 | private AddressableClient addressableClient; 71 | 72 | // service initialization 73 | @Value("${service.connect.retries}") 74 | private int initRetries; 75 | 76 | @Value("${service.connect.interval}") 77 | private long initInterval; 78 | 79 | // track initialization attempts 80 | private int initAttempts; 81 | 82 | // track initialization success 83 | private boolean initialized; 84 | 85 | // track registration success 86 | private boolean registered; 87 | 88 | public BaseService() { 89 | setInitAttempts(0); 90 | setInitialized(false); 91 | } 92 | 93 | public int getInitAttempts() { 94 | return initAttempts; 95 | } 96 | 97 | public void setInitAttempts(int initAttempts) { 98 | this.initAttempts = initAttempts; 99 | } 100 | 101 | public int getInitRetries() { 102 | return initRetries; 103 | } 104 | 105 | public void setInitRetries(int initRetries) { 106 | this.initRetries = initRetries; 107 | } 108 | 109 | public long getInitInterval() { 110 | return initInterval; 111 | } 112 | 113 | public void setInitInterval(long initInterval) { 114 | this.initInterval = initInterval; 115 | } 116 | 117 | public boolean isInitialized() { 118 | return initialized; 119 | } 120 | 121 | public void setInitialized(boolean initialized) { 122 | this.initialized = initialized; 123 | } 124 | 125 | public boolean isRegistered() { 126 | return registered; 127 | } 128 | 129 | public void setRegistered(boolean registered) { 130 | logger.info("Service registered with id: " + service.getId()); 131 | this.registered = registered; 132 | } 133 | 134 | // The base implementation always succeeds, derived classes customize 135 | public boolean initialize(String deviceServiceId) { 136 | return true; 137 | } 138 | 139 | @PostConstruct 140 | private void postConstructInitialize() { 141 | logger.debug("post construction initialization"); 142 | attemptToInitialize(); 143 | } 144 | 145 | @Async 146 | public void attemptToInitialize() { 147 | 148 | // count the attempt 149 | setInitAttempts(getInitAttempts() + 1); 150 | logger.debug("initialization attempt " + getInitAttempts()); 151 | 152 | // first - get the service information or register service with metadata 153 | if (getService() != null) { 154 | // if we were able to get the service data we're registered 155 | setRegistered(true); 156 | // second - invoke any custom initialization method 157 | setInitialized(initialize(getServiceId())); 158 | } 159 | 160 | // if both are successful, then we're done 161 | if (isRegistered() && isInitialized()) { 162 | logger.info("initialization successful."); 163 | } else { 164 | // otherwise see if we need to keep going 165 | if ((getInitRetries() == 0) || (getInitAttempts() < getInitRetries())) { 166 | logger.debug("initialization unsuccessful. sleeping " + getInitInterval()); 167 | try { 168 | Thread.sleep(getInitInterval()); 169 | } catch (InterruptedException e) { 170 | Thread.currentThread().interrupt(); 171 | } 172 | // start up the next thread 173 | attemptToInitialize(); 174 | 175 | } else { 176 | // here, we've failed and run out of retries, so just be done. 177 | logger.info( 178 | "initialization unsuccessful after " + getInitAttempts() + " attempts. Giving up."); 179 | // TODO: what do we do here? exit? 180 | Application.exit(-1); 181 | } 182 | } 183 | } 184 | 185 | public String getHost() { 186 | return host; 187 | } 188 | 189 | public void setHost(String host) { 190 | this.host = host; 191 | } 192 | 193 | public int getPort() { 194 | return port; 195 | } 196 | 197 | public void setPort(int port) { 198 | this.port = port; 199 | } 200 | 201 | public String[] getLabels() { 202 | return labels; 203 | } 204 | 205 | public void setLabels(String[] labels) { 206 | this.labels = labels; 207 | } 208 | 209 | public String getCallbackUrl() { 210 | return callbackUrl; 211 | } 212 | 213 | public void setCallbackUrl(String callbackUrl) { 214 | this.callbackUrl = callbackUrl; 215 | } 216 | 217 | public String getServiceName() { 218 | return serviceName; 219 | } 220 | 221 | public void setServiceName(String serviceName) { 222 | this.serviceName = serviceName; 223 | } 224 | 225 | public DeviceService getService() { 226 | if (service == null) { 227 | try { 228 | service = deviceServiceClient.deviceServiceForName(serviceName); 229 | setService(service); 230 | logger.info("service " + serviceName + " has service id " + service.getId()); 231 | } catch (NotFoundException n) { 232 | try { 233 | setService(); 234 | } catch (NotFoundException e) { 235 | logger 236 | .info("failed to create service " + serviceName + " in metadata: " + e.getMessage()); 237 | service = null; 238 | } 239 | } catch (Exception e) { 240 | logger.error( 241 | "unable to establish connection to metadata " + e.getCause() + " " + e.getMessage()); 242 | service = null; 243 | } 244 | } 245 | return service; 246 | } 247 | 248 | private void setService() { 249 | logger.info("creating service " + serviceName + " in metadata"); 250 | service = new DeviceService(); 251 | 252 | // Check for an addressable 253 | Addressable addressable = null; 254 | try { 255 | addressable = addressableClient.addressableForName(serviceName); 256 | } catch (NotFoundException e) { 257 | // ignore this and create a new addressable 258 | } 259 | if (addressable == null) { 260 | addressable = new Addressable(serviceName, Protocol.HTTP, host, callbackUrl, port); 261 | addressable.setOrigin(System.currentTimeMillis()); 262 | try { 263 | addressableClient.add(addressable); 264 | } catch (NotFoundException e) { 265 | logger.error("Could not add addressable to metadata: " + e.getMessage()); 266 | service = null; 267 | return; 268 | } 269 | } 270 | 271 | // Setup the service 272 | service.setAddressable(addressable); 273 | service.setOrigin(System.currentTimeMillis()); 274 | service.setAdminState(AdminState.UNLOCKED); 275 | service.setOperatingState(OperatingState.ENABLED); 276 | service.setLabels(labels); 277 | service.setName(serviceName); 278 | try { 279 | String id = deviceServiceClient.add(service); 280 | service.setId(id); 281 | } catch (NotFoundException e) { 282 | logger.error("Could not add device service to metadata: " + e.getMessage()); 283 | service = null; 284 | } 285 | } 286 | 287 | public void setService(DeviceService srv) { 288 | service = srv; 289 | service.setAddressable(srv.getAddressable()); 290 | service.setLabels(srv.getLabels()); 291 | setHost(srv.getAddressable().getAddress()); 292 | setPort(srv.getAddressable().getPort()); 293 | setCallbackUrl(srv.getAddressable().getPath()); 294 | setServiceName(srv.getName()); 295 | setLabels(srv.getLabels()); 296 | } 297 | 298 | public boolean isServiceLocked() { 299 | DeviceService srv = getService(); 300 | if (srv == null) { 301 | return true; 302 | } 303 | return srv.getAdminState().equals(AdminState.LOCKED); 304 | } 305 | 306 | public String getServiceId() { 307 | return service.getId(); 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /src/main/java/org/edgexfoundry/data/DeviceStore.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016-2017 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | * @microservice: device-mqtt 15 | * @author: Jim White, Dell 16 | * @version: 1.0.0 17 | *******************************************************************************/ 18 | 19 | package org.edgexfoundry.data; 20 | 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.stream.Collectors; 25 | 26 | import org.edgexfoundry.controller.AddressableClient; 27 | import org.edgexfoundry.controller.DeviceClient; 28 | import org.edgexfoundry.controller.DeviceProfileClient; 29 | import org.edgexfoundry.domain.meta.Addressable; 30 | import org.edgexfoundry.domain.meta.AdminState; 31 | import org.edgexfoundry.domain.meta.Device; 32 | import org.edgexfoundry.domain.meta.DeviceProfile; 33 | import org.edgexfoundry.domain.meta.OperatingState; 34 | import org.edgexfoundry.exception.controller.NotFoundException; 35 | import org.edgexfoundry.handler.MqttHandler; 36 | import org.edgexfoundry.support.logging.client.EdgeXLogger; 37 | import org.edgexfoundry.support.logging.client.EdgeXLoggerFactory; 38 | import org.springframework.beans.factory.annotation.Autowired; 39 | import org.springframework.beans.factory.annotation.Value; 40 | import org.springframework.stereotype.Repository; 41 | 42 | @Repository 43 | public class DeviceStore { 44 | private static final EdgeXLogger logger = EdgeXLoggerFactory.getEdgeXLogger(DeviceStore.class); 45 | 46 | @Autowired 47 | private DeviceClient deviceClient; 48 | 49 | @Autowired 50 | private AddressableClient addressableClient; 51 | 52 | @Autowired 53 | private DeviceProfileClient profileClient; 54 | 55 | @Autowired 56 | private MqttHandler mqtt; 57 | 58 | @Autowired 59 | private WatcherStore watchers; 60 | 61 | @Autowired 62 | private ProfileStore profiles; 63 | 64 | @Value("${service.name}") 65 | private String serviceName; 66 | 67 | // cache for devices 68 | private Map devices = new HashMap<>(); 69 | 70 | public boolean remove(Device device) { 71 | logger.debug("Removing managed device: " + device.getName()); 72 | if (devices.containsKey(device.getName())) { 73 | devices.remove(device.getName()); 74 | mqtt.disconnectDevice(device); 75 | deviceClient.updateOpState(device.getId(), OperatingState.DISABLED.name()); 76 | profiles.removeDevice(device); 77 | } 78 | return true; 79 | } 80 | 81 | public boolean remove(String deviceId) { 82 | Device d = devices.values().stream().filter(device -> device.getId().equals(deviceId)).findAny() 83 | .orElse(null); 84 | 85 | if (d != null) { 86 | remove(d); 87 | } 88 | 89 | return true; 90 | } 91 | 92 | public boolean add(String deviceId) { 93 | Device device = deviceClient.device(deviceId); 94 | return add(device); 95 | } 96 | 97 | public boolean add(Device device) { 98 | if (devices.containsKey(device.getName())) { 99 | devices.remove(device.getName()); 100 | profiles.removeDevice(device); 101 | } 102 | 103 | logger.info("Adding managed device: " + device.getName()); 104 | Device metaDevice = addDeviceToMetaData(device); 105 | 106 | if (metaDevice == null) { 107 | remove(device); 108 | return false; 109 | } 110 | 111 | if (metaDevice.getOperatingState().equals(OperatingState.ENABLED)) { 112 | mqtt.initializeDevice(metaDevice); 113 | } 114 | 115 | return true; 116 | } 117 | 118 | private Device addDeviceToMetaData(Device device) { 119 | // Create a new addressable Object with the devicename + last 6 digits of MAC address. 120 | // Assume this to be unique 121 | 122 | Addressable addressable = null; 123 | try { 124 | addressableClient.addressableForName(device.getAddressable().getName()); 125 | } catch (javax.ws.rs.NotFoundException e) { 126 | addressable = device.getAddressable(); 127 | addressable.setOrigin(System.currentTimeMillis()); 128 | logger.info("Creating new Addressable Object with name: " + addressable.getName() 129 | + ", Address:" + addressable); 130 | String addressableId = addressableClient.add(addressable); 131 | addressable.setId(addressableId); 132 | device.setAddressable(addressable); 133 | } 134 | 135 | Device d = null; 136 | try { 137 | d = deviceClient.deviceForName(device.getName()); 138 | device.setId(d.getId()); 139 | if (!device.getOperatingState().equals(d.getOperatingState())) { 140 | deviceClient.updateOpState(device.getId(), device.getOperatingState().name()); 141 | } 142 | } catch (javax.ws.rs.NotFoundException e) { 143 | logger.info("Adding Device to Metadata:" + device.getName()); 144 | try { 145 | device.setId(deviceClient.add(device)); 146 | } catch (Exception f) { 147 | logger.error("Could not add new device " + device.getName() + " to metadata with error " 148 | + e.getMessage()); 149 | return null; 150 | } 151 | } 152 | 153 | profiles.addDevice(device); 154 | devices.put(device.getName(), device); 155 | return device; 156 | } 157 | 158 | public boolean update(String deviceId) { 159 | Device device = deviceClient.device(deviceId); 160 | Device localDevice = getDeviceById(deviceId); 161 | if (device != null && localDevice != null && compare(device, localDevice)) { 162 | return true; 163 | } 164 | 165 | return add(device); 166 | } 167 | 168 | private boolean compare(Device a, Device b) { 169 | if (a.getAddressable().equals(b.getAddressable()) 170 | && a.getAdminState().equals(a.getAdminState()) 171 | && a.getDescription().equals(b.getDescription()) && a.getId().equals(b.getId()) 172 | && a.getLabels().equals(b.getLabels()) && a.getLocation().equals(b.getLocation()) 173 | && a.getName().equals(b.getName()) && a.getOperatingState().equals(b.getOperatingState()) 174 | && a.getProfile().equals(b.getProfile()) && a.getService().equals(b.getService())) { 175 | return true; 176 | } 177 | 178 | return false; 179 | } 180 | 181 | public Map getDevices() { 182 | return devices; 183 | } 184 | 185 | public Map initialize(String id) { 186 | devices = new HashMap<>(); 187 | watchers.initialize(id); 188 | mqtt.initialize(); 189 | List metaDevices = deviceClient.devicesForService(id); 190 | for (Device device : metaDevices) { 191 | deviceClient.updateOpState(device.getId(), OperatingState.DISABLED.name()); 192 | add(device); 193 | } 194 | 195 | logger.info("Device service has " + devices.size() + " devices."); 196 | return getDevices(); 197 | } 198 | 199 | public List getMetaDevices() { 200 | List metaDevices; 201 | metaDevices = deviceClient.devicesForServiceByName(serviceName); 202 | for (Device metaDevice : metaDevices) { 203 | Device device = devices.get(metaDevice.getName()); 204 | 205 | if (device != null) { 206 | device.setOperatingState(metaDevice.getOperatingState()); 207 | } 208 | } 209 | return metaDevices; 210 | } 211 | 212 | public Device getMetaDevice(String deviceName) { 213 | List metaDevices = getMetaDevices(); 214 | Device result = metaDevices.stream().filter(device -> deviceName.equals(device.getName())) 215 | .findAny().orElse(null); 216 | return result; 217 | } 218 | 219 | public Device getMetaDeviceById(String deviceId) { 220 | List metaDevices = getMetaDevices(); 221 | Device result = metaDevices.stream().filter(device -> deviceId.equals(device.getId())).findAny() 222 | .orElse(null); 223 | return result; 224 | } 225 | 226 | public Device getDevice(String deviceName) { 227 | if (devices != null) { 228 | return devices.get(deviceName); 229 | } else { 230 | return null; 231 | } 232 | } 233 | 234 | public Device getDeviceById(String deviceId) { 235 | if (devices != null) { 236 | return devices.values().stream().filter(device -> device.getId().equals(deviceId)).findAny() 237 | .orElse(null); 238 | } 239 | 240 | return null; 241 | } 242 | 243 | public boolean isDeviceLocked(String deviceId) { 244 | Device device = getDeviceById(deviceId); 245 | if (device == null) { 246 | device = getMetaDeviceById(deviceId); 247 | if (device == null) { 248 | logger.error("Device not present with id " + deviceId); 249 | throw new NotFoundException("device", deviceId); 250 | } 251 | } 252 | 253 | return device.getAdminState().equals(AdminState.LOCKED) 254 | || device.getOperatingState().equals(OperatingState.DISABLED); 255 | } 256 | 257 | public void setDeviceOpState(String deviceName, OperatingState state) { 258 | deviceClient.updateOpStateByName(deviceName, state.name()); 259 | } 260 | 261 | public void setDeviceByIdOpState(String deviceId, OperatingState state) { 262 | deviceClient.updateOpState(deviceId, state.name()); 263 | } 264 | 265 | public boolean updateProfile(String profileId) { 266 | DeviceProfile profile; 267 | try { 268 | profile = profileClient.deviceProfile(profileId); 269 | } catch (Exception e) { 270 | // No such profile exists to update 271 | return true; 272 | } 273 | 274 | boolean success = true; 275 | for (Device device : devices.entrySet().stream().map(d -> d.getValue()) 276 | .filter(d -> profile.getName().equals(d.getProfile().getName())) 277 | .collect(Collectors.toList())) { 278 | // update all devices that use the profile 279 | device.setProfile(profile); 280 | success &= update(device.getId()); 281 | } 282 | return success; 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/test/resources/device-mqtt.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | title: device-mqtt 3 | version: v1 4 | baseUri: "http://device-mqtt:49982/api/{version}" 5 | schemas: 6 | - 7 | responseobjects: '{"type":"array","$schema":"http://json-schema.org/draft-03/schema#","title":"responseobjects","items":{"type":"object","required":false,"$ref":"#/schemas/responseobject"}}' 8 | - 9 | responseobject: '{"type":"object","$schema":"http://json-schema.org/draft-03/schema#","title":"responseobject","properties":{"ValueDescriptorName":{"type":"object","required":false,"title":"ValueDescriptorName"}}}' 10 | - 11 | callbackalert: '{"type": "object","$schema": "http://json-schema.org/draft-03/schema# 12 | ","title": "Notification Schema","properties": {"id": {"description": "the identifier of the object which is called back","type": "string"},"actionType": {"description": "the type of the called back object","enum":["PROFILE","DEVICE","PROVISIONWATCHER","SCHEDULE","SCHEDULEEVENT"],"type": "string"},"required": ["id"]}}' 13 | /ping: 14 | displayName: Ping Resource 15 | description: Example - http://localhost:49982/api/{version}/ping 16 | get: 17 | description: Test service providing an indication that the service is available. 18 | displayName: service up check 19 | responses: 20 | "200": 21 | description: return value of "pong" 22 | "503": 23 | description: for unknown or unanticipated issues 24 | /device/{id}/{command}: 25 | displayName: Command Device (by id) with command name 26 | description: Example - http://localhost:49982/api/{version}/device/57bd0f2d32d258ad3fcd2d4b/Command 27 | uriParameters: 28 | id: 29 | displayName: id 30 | type: string 31 | required: false 32 | repeat: false 33 | command: 34 | displayName: command 35 | type: string 36 | required: false 37 | repeat: false 38 | get: 39 | description: Issue the get command referenced by the command to the device/sensor (referenced by database generated id) it is associated to via the device service. ServiceException (HTTP 503) for unanticipated or unknown issues encountered. Throws NotFoundException (HTTP 404) if no device exists by the id provided. Throws LockedException (HTTP 423) if the device or service is locked (admin state) or disabled (operating state). 40 | body: 41 | application/json: 42 | schema: responseobject 43 | example: '{"VDS-CurrentTemperature": "32.52"}' 44 | responses: 45 | "200": 46 | description: String as returned by the device/sensor via the device service. 47 | "404": 48 | description: if no device exists by the id provided 49 | "423": 50 | description: if the device or service is locked (admin state) or disabled (operating state) 51 | "503": 52 | description: for unanticipated or unknown issues encountered. 53 | put: 54 | description: Issue the put command referenced by the command to the device/sensor (referenced by database generated id) it is associated to via the device service. ServiceException (HTTP 503) for unanticipated or unknown issues encountered. Throws NotFoundException (HTTP 404) if no device exists by the id provided. Throws LockedException (HTTP 423) if the device or service is locked (admin state). 55 | body: 56 | application/json: 57 | schema: responseobject 58 | example: '{"VDS-CurrentTemperature": "32.52"}' 59 | responses: 60 | "200": 61 | description: String as returned by the device/sensor via the device service. 62 | "404": 63 | description: if no device exists by the id provided 64 | "423": 65 | description: if the device or service is locked (admin state) or disabled (operating state) 66 | "503": 67 | description: for unanticipated or unknown issues encountered. 68 | post: 69 | description: Issue the put command referenced by the command to the device/sensor (referenced by database generated id) it is associated to via the device service. ServiceException (HTTP 503) for unanticipated or unknown issues encountered. Throws NotFoundException (HTTP 404) if no device exists by the id provided. Throws LockedException (HTTP 423) if the device or service is locked (admin state). 70 | body: 71 | application/json: 72 | schema: responseobject 73 | example: '{"VDS-CurrentTemperature": "32.52"}' 74 | responses: 75 | "200": 76 | description: String as returned by the device/sensor via the device service. 77 | "404": 78 | description: if no device exists by the id provided 79 | "423": 80 | description: if the device or service is locked (admin state) or disabled (operating state) 81 | "503": 82 | description: for unanticipated or unknown issues encountered. 83 | /device/all/{command}: 84 | displayName: Command all operational Devices for the service with command name 85 | description: Example - http://localhost:49982/api/{version}/device/all/Command 86 | uriParameters: 87 | command: 88 | displayName: command 89 | type: string 90 | required: false 91 | repeat: false 92 | get: 93 | description: Issue the get command referenced by the command to all operational device(s)/sensor(s) that are associated to the device service and have this command. ServiceException (HTTP 503) for unanticipated or unknown issues encountered. Throws LockedException (HTTP 423) if the device service is locked (admin state). 94 | body: 95 | application/json: 96 | schema: responseobjects 97 | example: '[{"VDS-CurrentTemperature": "32.52"},{"VDS-CurrentHumidity": "1.0"}]' 98 | responses: 99 | "200": 100 | description: String as returned by the device(s)/sensor(s) via the device service. 101 | "423": 102 | description: if the device service is locked (admin state) 103 | "503": 104 | description: for unanticipated or unknown issues encountered. 105 | put: 106 | description: Issue the put command referenced by the command to all operational device(s)/sensor(s) that are associated to the device service and have this command. ServiceException (HTTP 503) for unanticipated or unknown issues encountered. Throws LockedException (HTTP 423) if the device service is locked (admin state). 107 | body: 108 | application/json: 109 | schema: responseobjects 110 | example: '[{"VDS-CurrentTemperature": "32.52"},{"VDS-CurrentHumidity": "1.0"}]' 111 | responses: 112 | "200": 113 | description: String as returned by the device(s)/sensor(s) via the device service. 114 | "423": 115 | description: if the device service is locked (admin state) 116 | "503": 117 | description: for unanticipated or unknown issues encountered. 118 | post: 119 | description: Issue the put command referenced by the command to all operational device(s)/sensor(s) that are associated to the device service and have this command. ServiceException (HTTP 503) for unanticipated or unknown issues encountered. Throws LockedException (HTTP 423) if the device service is locked (admin state). 120 | body: 121 | application/json: 122 | schema: responseobjects 123 | example: '[{"VDS-CurrentTemperature": "32.52"},{"VDS-CurrentHumidity": "1.0"}]' 124 | responses: 125 | "200": 126 | description: String as returned by the device(s)/sensor(s) via the device service. 127 | "423": 128 | description: if the device service is locked (admin state) 129 | "503": 130 | description: for unanticipated or unknown issues encountered. 131 | /callback: 132 | displayName: Update Callback 133 | description: Example - http://localhost:49982/api/{version}/callback 134 | post: 135 | description: Add the object referred to by the database id. Returns ServiceException (HTTP 503) for unknown or unanticipated issues. Returns NotFoundException (HTTP 404) if the object cannot be found by the id provided in metadata. 136 | responses: 137 | "200": 138 | description: boolean indicating success of the operation 139 | "503": 140 | description: for unknown or unanticipated issues. 141 | "404": 142 | description: if the object cannot be found by the id provided. 143 | put: 144 | description: Update the object referred to by the database id. Returns ServiceException (HTTP 503) for unknown or unanticipated issues. Returns NotFoundException (HTTP 404) if the object cannot be found by the id provided. 145 | responses: 146 | "200": 147 | description: boolean indicating success of the operation 148 | "503": 149 | description: for unknown or unanticipated issues. 150 | "404": 151 | description: if the object cannot be found by the id provided. 152 | delete: 153 | description: Delete the object referred to by the database id. Returns ServiceException (HTTP 503) for unknown or unanticipated issues. Returns NotFoundException (HTTP 404) if the object cannot be found by the id provided. 154 | responses: 155 | "200": 156 | description: boolean indicating success of the operation 157 | "503": 158 | description: for unknown or unanticipated issues. 159 | "404": 160 | description: if the object cannot be found by the id provided. 161 | /discovery: 162 | displayName: Run device discovery request for all registered Provision Watchers 163 | description: Example - http://localhost:49982/api/{version}/discovery 164 | post: 165 | description: Run the discovery request for a device service. Does nothing unlesss the driver functionality is instrumented. Returns ServiceException (HTTP 503) for unknown or unanticipated issues. 166 | responses: 167 | "200": 168 | description: the service is running the discovery request 169 | "503": 170 | description: for unknown or unanticipated issues. 171 | /debug/transformData/{transformData}: 172 | displayName: Set the Device Service data transformation state 173 | description: Example - http://localhost:49982/api/{version}/debug/transformData/false (sets the device service to not transform data to or from the devices) 174 | uriParameters: 175 | transformData: 176 | displayName: transformData 177 | type: boolean 178 | required: false 179 | repeat: false 180 | get: 181 | description: Tells the device service to pass data modified or unmodified to and from the devices. Returns ServiceException (HTTP 503) for unknown or unanticipated issues. 182 | responses: 183 | "200": 184 | description: set the data tranformation state successfully 185 | "503": 186 | description: for unknown or unanticipated issues. 187 | -------------------------------------------------------------------------------- /LICENSE-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2017 Dell, Inc. 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------