Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport: error handling for unexpected error in DefaultRegistrationEngin (modified) #945

Merged
merged 8 commits into from
Dec 10, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.eclipse.leshan.client.engine.RegistrationEngineFactory;
import org.eclipse.leshan.client.engine.RegistrationEngineFactory2;
import org.eclipse.leshan.client.observer.LwM2mClientObserver;
import org.eclipse.leshan.client.observer.LwM2mClientObserverAdapter;
import org.eclipse.leshan.client.observer.LwM2mClientObserverDispatcher;
import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
import org.eclipse.leshan.client.resource.LwM2mObjectTree;
Expand Down Expand Up @@ -117,7 +118,14 @@ protected LwM2mObjectTree createObjectTree(List<? extends LwM2mObjectEnabler> ob
}

protected LwM2mClientObserverDispatcher createClientObserverDispatcher() {
return new LwM2mClientObserverDispatcher();
LwM2mClientObserverDispatcher observer = new LwM2mClientObserverDispatcher();
observer.addObserver(new LwM2mClientObserverAdapter() {
@Override
public void onUnexpectedError(Throwable unexpectedError) {
LeshanClient.this.destroy(false);
}
});
return observer;
}

protected BootstrapHandler createBoostrapHandler(LwM2mObjectTree objectTree) {
Expand Down Expand Up @@ -207,6 +215,7 @@ public void start() {
LOG.info("Starting Leshan client ...");
endpointsManager.start();
engine.start();
objectTree.start();
if (LOG.isInfoEnabled()) {
LOG.info("Leshan client[endpoint:{}] started.", engine.getEndpoint());
}
Expand All @@ -217,6 +226,7 @@ public void stop(boolean deregister) {
LOG.info("Stopping Leshan Client ...");
engine.stop(deregister);
endpointsManager.stop();
objectTree.stop();
LOG.info("Leshan client stopped.");
}

Expand All @@ -226,6 +236,7 @@ public void destroy(boolean deregister) {
engine.destroy(deregister);
endpointsManager.destroy();
requestSender.destroy();
objectTree.destroy();
LOG.info("Leshan client destroyed.");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.eclipse.leshan.client.RegistrationUpdate;
import org.eclipse.leshan.client.bootstrap.BootstrapHandler;
import org.eclipse.leshan.client.observer.LwM2mClientObserver;
import org.eclipse.leshan.client.observer.LwM2mClientObserver2;
import org.eclipse.leshan.client.request.LwM2mRequestSender;
import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
import org.eclipse.leshan.client.resource.LwM2mObjectTree;
Expand Down Expand Up @@ -517,6 +518,9 @@ public void run() {
LOG.info("Bootstrap task interrupted. ");
} catch (RuntimeException e) {
LOG.error("Unexpected exception during bootstrap task", e);
if (observer instanceof LwM2mClientObserver2) {
((LwM2mClientObserver2) observer).onUnexpectedError(e);
}
}
}
}
Expand Down Expand Up @@ -555,6 +559,9 @@ public void run() {
LOG.info("Registration task interrupted. ");
} catch (RuntimeException e) {
LOG.error("Unexpected exception during registration task", e);
if (observer instanceof LwM2mClientObserver2) {
((LwM2mClientObserver2) observer).onUnexpectedError(e);
}
}
}
}
Expand Down Expand Up @@ -603,6 +610,9 @@ public void run() {
LOG.info("Registration update task interrupted.");
} catch (RuntimeException e) {
LOG.error("Unexpected exception during update registration task", e);
if (observer instanceof LwM2mClientObserver2) {
((LwM2mClientObserver2) observer).onUnexpectedError(e);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.eclipse.leshan.client.observer;

/**
* An extended interface of {@link LwM2mClientObserver} with handler for unexpected error.
* <p>
* Future plan: Since version 2.0, this interface is going to merge into {@link LwM2mClientObserver}.
*
* @since 1.3
*/
public interface LwM2mClientObserver2 extends LwM2mClientObserver {
// ============== Unexpected Error Handling =================

void onUnexpectedError(Throwable unexpectedError);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
* An abstract adapter class for observing registration life cycle. The methods in this class are empty. This class
* exists as convenience for creating client observer objects.
*/
public class LwM2mClientObserverAdapter implements LwM2mClientObserver {
public class LwM2mClientObserverAdapter implements LwM2mClientObserver2 {

@Override
public void onBootstrapStarted(ServerIdentity bsserver, BootstrapRequest request) {
Expand Down Expand Up @@ -95,4 +95,8 @@ public void onDeregistrationFailure(ServerIdentity server, DeregisterRequest req
@Override
public void onDeregistrationTimeout(ServerIdentity server, DeregisterRequest request) {
}

@Override
public void onUnexpectedError(Throwable unexpectedError) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
* A dispatcher for LwM2mClientObserver. It allow several observers on a LwM2mClient.
*
*/
public class LwM2mClientObserverDispatcher implements LwM2mClientObserver {
public class LwM2mClientObserverDispatcher implements LwM2mClientObserver2 {
private CopyOnWriteArrayList<LwM2mClientObserver> observers = new CopyOnWriteArrayList<>();

public void addObserver(LwM2mClientObserver observer) {
Expand Down Expand Up @@ -151,4 +151,12 @@ public void onDeregistrationTimeout(ServerIdentity server, DeregisterRequest req
}
}

@Override
public void onUnexpectedError(Throwable unexpectedError) {
for (LwM2mClientObserver observer : observers) {
if (observer instanceof LwM2mClientObserver2) {
((LwM2mClientObserver2) observer).onUnexpectedError(unexpectedError);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@
* <p>
* Implementations of this interface should adhere to the definition of the implemented LWM2M Object type regarding
* acceptable resource IDs for the <code>read, write</code> and <code>execute</code> methods.
* <p>
* {@code LeshanClient#destroy()} is called, {@code LwM2mInstanceEnabler#destroy()} is also called if it implements the
* {@link org.eclipse.leshan.core.Destroyable} interface.
* And {@link org.eclipse.leshan.core.Startable} ({@code #start()}) and {@link org.eclipse.leshan.core.Stoppable} ({@code #stop()})
* are also same as this.
* If you need to restart the instance, please implement {@link org.eclipse.leshan.core.Startable} with
* {@link org.eclipse.leshan.core.Stoppable} together.
*/
public interface LwM2mInstanceEnabler {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@
* <p>
* In case you really need the flexibility of this interface you should consider to inherit from
* {@link BaseObjectEnabler}.
* <p>
* An instance that implements this interface synchronizes with the lifecycle of the LeshanClient.
* This means when {@code LeshanClient#destroy()} is called, {@code LwM2mObjectEnabler#destroy()} is
* also called if it implements the {@link org.eclipse.leshan.core.Destroyable} interface.
* And {@link org.eclipse.leshan.core.Startable} ({@code #start()}) and
* {@link org.eclipse.leshan.core.Stoppable} ({@code #stop()}) are also same as this.
* If you need to restart the instance, please implement {@link org.eclipse.leshan.core.Startable}
* with {@link org.eclipse.leshan.core.Stoppable} together.
*/
public interface LwM2mObjectEnabler {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,17 @@
import org.eclipse.leshan.client.LwM2mClient;
import org.eclipse.leshan.client.resource.listener.ObjectListener;
import org.eclipse.leshan.client.resource.listener.ObjectsListener;
import org.eclipse.leshan.core.Destroyable;
import org.eclipse.leshan.core.Startable;
import org.eclipse.leshan.core.Stoppable;

/**
* The LWM2M Object Tree.
* <p>
* It contains all the {@link LwM2mObjectEnabler} which are the implementation of each LWM2M object supported by the
* client.
*/
public class LwM2mObjectTree {
public class LwM2mObjectTree implements Startable, Stoppable, Destroyable {

protected ObjectListener dispatcher = new ObjectListenerDispatcher();
protected CopyOnWriteArrayList<ObjectsListener> listeners = new CopyOnWriteArrayList<>();
Expand Down Expand Up @@ -94,6 +97,35 @@ public void removeObjectEnabler(int objectId) {
}
}

@Override
public void destroy() {
for (LwM2mObjectEnabler objectEnabler : objectEnablers.values()) {
if (objectEnabler instanceof Destroyable) {
((Destroyable) objectEnabler).destroy();
} else if (objectEnabler instanceof Stoppable) {
((Stoppable) objectEnabler).stop();
}
}
}

@Override
public void start() {
for (LwM2mObjectEnabler objectEnabler : objectEnablers.values()) {
if (objectEnabler instanceof Startable) {
((Startable) objectEnabler).start();
}
}
}

@Override
public void stop() {
for (LwM2mObjectEnabler objectEnabler : objectEnablers.values()) {
if (objectEnabler instanceof Stoppable) {
((Stoppable) objectEnabler).stop();
}
}
}

protected class ObjectListenerDispatcher implements ObjectListener {
@Override
public void objectInstancesAdded(LwM2mObjectEnabler object, int... instanceIds) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
import org.eclipse.leshan.client.LwM2mClient;
import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.client.servers.ServersInfoExtractor;
import org.eclipse.leshan.core.Destroyable;
import org.eclipse.leshan.core.LwM2mId;
import org.eclipse.leshan.core.Startable;
import org.eclipse.leshan.core.Stoppable;
import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.node.LwM2mObject;
import org.eclipse.leshan.core.node.LwM2mObjectInstance;
Expand Down Expand Up @@ -61,7 +64,7 @@
* Implementing a {@link LwM2mInstanceEnabler} then creating an {@link ObjectEnabler} with {@link ObjectsInitializer} is
* the easier way to implement LWM2M object in Leshan client.
*/
public class ObjectEnabler extends BaseObjectEnabler {
public class ObjectEnabler extends BaseObjectEnabler implements Destroyable, Startable, Stoppable {

protected Map<Integer, LwM2mInstanceEnabler> instances;
protected LwM2mInstanceEnablerFactory instanceFactory;
Expand Down Expand Up @@ -399,4 +402,33 @@ public void setLwM2mClient(LwM2mClient client) {
instanceEnabler.setLwM2mClient(client);
}
}

@Override
public void destroy() {
for (LwM2mInstanceEnabler instanceEnabler : instances.values()) {
if (instanceEnabler instanceof Destroyable) {
((Destroyable) instanceEnabler).destroy();
} else if (instanceEnabler instanceof Stoppable) {
((Stoppable) instanceEnabler).stop();
}
}
}

@Override
public void start() {
for (LwM2mInstanceEnabler instanceEnabler : instances.values()) {
if (instanceEnabler instanceof Startable) {
((Startable) instanceEnabler).start();
}
}
}

@Override
public void stop() {
for (LwM2mInstanceEnabler instanceEnabler : instances.values()) {
if (instanceEnabler instanceof Stoppable) {
((Stoppable) instanceEnabler).stop();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import org.eclipse.leshan.client.resource.BaseInstanceEnabler;
import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.Destroyable;
import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel.Type;
import org.eclipse.leshan.core.node.LwM2mResource;
Expand All @@ -23,17 +24,19 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyDevice extends BaseInstanceEnabler {
public class MyDevice extends BaseInstanceEnabler implements Destroyable {

private static final Logger LOG = LoggerFactory.getLogger(MyDevice.class);

private static final Random RANDOM = new Random();
private static final List<Integer> supportedResources = Arrays.asList(0, 1, 2, 3, 9, 10, 11, 13, 14, 15, 16, 17, 18,
19, 20, 21);

private final Timer timer;

public MyDevice() {
// notify new date each 5 second
Timer timer = new Timer("Device-Current Time");
this.timer = new Timer("Device-Current Time");
timer.schedule(new TimerTask() {
@Override
public void run() {
Expand Down Expand Up @@ -209,4 +212,9 @@ private long getMemoryTotal() {
public List<Integer> getAvailableResourceIds(ObjectModel model) {
return supportedResources;
}

@Override
public void destroy() {
timer.cancel();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@

import org.eclipse.leshan.client.resource.BaseInstanceEnabler;
import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.Destroyable;
import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.response.ExecuteResponse;
import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.core.util.NamedThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RandomTemperatureSensor extends BaseInstanceEnabler {
public class RandomTemperatureSensor extends BaseInstanceEnabler implements Destroyable {

private static final Logger LOG = LoggerFactory.getLogger(RandomTemperatureSensor.class);

Expand Down Expand Up @@ -113,4 +114,9 @@ private void resetMinMaxMeasuredValues() {
public List<Integer> getAvailableResourceIds(ObjectModel model) {
return supportedResources;
}

@Override
public void destroy() {
scheduler.shutdown();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.eclipse.leshan.core;

public interface Destroyable {
/** Destroy the instances and frees all system resources. */
void destroy();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.eclipse.leshan.core;

public interface Startable {
void start();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.eclipse.leshan.core;

public interface Stoppable {
void stop();
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.californium.core.server.resources.Resource;
import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.leshan.core.Destroyable;
import org.eclipse.leshan.core.Startable;
import org.eclipse.leshan.core.Stoppable;
import org.eclipse.leshan.core.californium.CoapResponseCallback;
import org.eclipse.leshan.core.node.codec.CodecException;
import org.eclipse.leshan.core.node.codec.LwM2mNodeDecoder;
Expand All @@ -44,9 +47,6 @@
import org.eclipse.leshan.core.response.LwM2mResponse;
import org.eclipse.leshan.core.response.ResponseCallback;
import org.eclipse.leshan.core.util.Validate;
import org.eclipse.leshan.server.Destroyable;
import org.eclipse.leshan.server.Startable;
import org.eclipse.leshan.server.Stoppable;
import org.eclipse.leshan.server.californium.observation.ObservationServiceImpl;
import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore;
import org.eclipse.leshan.server.californium.registration.RegisterResource;
Expand Down Expand Up @@ -89,7 +89,6 @@
* <p>
* The {@link LeshanServerBuilder} should be the preferred way to build an instance of {@link LeshanServer}.
*/
@SuppressWarnings("deprecation")
public class LeshanServer {

private static final Logger LOG = LoggerFactory.getLogger(LeshanServer.class);
Expand Down
Loading