Package ch.ethz.inf.vs.californium.server.resources

Source Code of ch.ethz.inf.vs.californium.server.resources.ResourceBase

/*******************************************************************************
* Copyright (c) 2014, Institute for Pervasive Computing, ETH Zurich.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Californium (Cf) CoAP framework.
******************************************************************************/
package ch.ethz.inf.vs.californium.server.resources;

import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.Semaphore;
import java.util.logging.Logger;

import ch.ethz.inf.vs.californium.CoapClient;
import ch.ethz.inf.vs.californium.coap.CoAP;
import ch.ethz.inf.vs.californium.coap.CoAP.Code;
import ch.ethz.inf.vs.californium.coap.CoAP.ResponseCode;
import ch.ethz.inf.vs.californium.coap.CoAP.Type;
import ch.ethz.inf.vs.californium.coap.Response;
import ch.ethz.inf.vs.californium.network.Endpoint;
import ch.ethz.inf.vs.californium.network.Exchange;
import ch.ethz.inf.vs.californium.observe.ObserveNotificationOrderer;
import ch.ethz.inf.vs.californium.observe.ObserveRelation;
import ch.ethz.inf.vs.californium.observe.ObserveRelationContainer;
import ch.ethz.inf.vs.californium.server.ServerMessageDeliverer;

/**
* ResourceBase is a basic implementation of a resource. Extend this class to
* write your own resources. Instances of type or subtype of ResourceBase can be
* built up to a tree very easily, see {@link #add(ResourceBase)}.
* <p>
* ResourceBase uses four distinct methods to handle requests:
* <tt>handleGET()</tt>, <tt>handlePOST()</tt>, <tt>handlePUT()</tt> and
* <tt>handleDELETE()</tt>. Each method has a default implementation that
* responds with a 4.05 (Method Not Allowed). Each method exists twice but with
* a different parameter: <tt>handleGET(Exchange)</tt> and
* <tt>handleGET(CoAPExchange)</tt> for instance. The class {@link Exchange} is
* used internally in Californium to keep the state of an exchange of CoAP
* messages. Only override this version of the method if you need to access
* detailed information of an exchange. Most developer should rather override
* the latter version. CoAPExchange provides a save and user-friendlier API that
* can be used to respond to a request.
* <p>
* The following example override the four handle-method.
* <pre>
* public class CoAPResourceExample extends ResourceBase {
*
*   public CoAPResourceExample(String name) {
*     super(name);
*   }
*
*   public void handleGET(CoapExchange exchange) {
*     exchange.respond("hello world");
*   }
*
*   public void handlePOST(CoapExchange exchange) {
*     exchange.accept();
*
*     List<String> queries = exchange.getRequestOptions().getURIQueries();
*     // ...
*     exchange.respond(ResponseCode.CREATED);
*   }
*
*   public void handlePUT(CoapExchange exchange) {
*     // ...
*     exchange.respond(ResponseCode.CHANGED);
*     changed(); // notify all observers
*   }
*
*   public void handleDELETE(CoapExchange exchange) {
*     delete();
*     exchange.respond(ResponseCode.DELETED);
*   }
* }
* </pre>
* <p>
* Each resource is allowed to define its own executor. When a request arrives,
* the request will be handled by the resource's executor. If a resource does
* not define its own executor, the executor of its parent or transitively an
* ancestor will be used. If no ancestor up to the root defines its own
* executor, the thread that delivers the request will invoke the handling
* method.
* <p>
* ResourceBase supports CoAP's observe mechanism. Enable a ResourceBase to be
* observable by a CoAP client by marking it as observable with
* {@link #setObservable(boolean)}. Notify all CoAP observers by calling
* {@link #changed()}. The method changed() reprocesses the requests from the
* observing clients that have originally established the observe relation. If
* the resource or one of its ancestors define an executor, the reprocessing is
* done on the executor. A CoAP observe relation between this resource and a
* CoAP client is represented by an instance of {@link ObserveRelation}.
* <p>
* In contrast the class {@link ResourceObserver} has nothing to do with CoAP's
* observe mechanism but is an implementation of the general observe-pattern. A
* ResourceObserver is invoked whenever the name or path of a resource changes,
* when a child resource is added or removed or when a CoAP observe relation is
* added or canceled.
* // TODO: make example with createClient().get()
*/
public  class ResourceBase implements Resource {

  /** The logger. */
  protected final static Logger LOGGER = Logger.getLogger(ResourceBase.class.getCanonicalName());
 
  /* The attributes of this resource. */
  private final ResourceAttributes attributes;
 
  /* The resource name. */
  private String name;
 
  /* The resource path. */
  private String path;
 
  /* Indicates whether this resource is visible to clients. */
  private boolean visible;
 
  /* Indicates whether this resource is observable by clients. */
  private boolean observable;
 
  /* The child resources.
   * We need a ConcurrentHashMap to have stronger guarantees in a
   * multi-threaded environment (e.g. for discovery to work properly).
   */
  private ConcurrentHashMap<String, Resource> children;
 
  /* The parent of this resource. */
  private Resource parent;
 
  /* The type used for notifications (no change when set to null) */
  private Type observeType = null;
 
  /* The list of observers (not CoAP observer). */
  private List<ResourceObserver> observers;

  /* The the list of CoAP observe relations. */
  private ObserveRelationContainer observeRelations;
 
  /* The notification orderer. */
  private ObserveNotificationOrderer notificationOrderer;
 
  /**
   * Constructs a new resource with the specified name.
   *
   * @param name the name
   */
  public ResourceBase(String name) {
    this(name, true);
  }
 
  /**
   * Constructs a new resource with the specified name and makes it visible to
   * clients if the flag is true.
   *
   * @param name the name
   * @param visible if the resource is visible
   */
  public ResourceBase(String name, boolean visible) {
    this.name = name;
    this.path = "";
    this.visible = visible;
    this.attributes = new ResourceAttributes();
    this.children = new ConcurrentHashMap<String, Resource>();
    this.observers = new CopyOnWriteArrayList<ResourceObserver>();
    this.observeRelations = new ObserveRelationContainer();
    this.notificationOrderer = new ObserveNotificationOrderer();
  }
 

  /**
   * Handles any request in the given exchange. By default it responds
   * with a 4.05 (Method Not Allowed). Override this method if your
   * resource handler requires advanced access to the internal Exchange class.
   * Most developer should be better off with overriding the called methods
   * {@link #handleGET(CoapExchange)}, {@link #handlePOST(CoapExchange)},
   * {@link #handlePUT(CoapExchange)}, and {@link #handleDELETE(CoapExchange)},
   * which provide a better API through the {@link CoapExchange} class.
   *
   * @param exchange the exchange with the request
   */
  @Override
  public void handleRequest(final Exchange exchange) {
    Code code = exchange.getRequest().getCode();
    switch (code) {
      case GET:  handleGET(new CoapExchange(exchange, this)); break;
      case POST:  handlePOST(new CoapExchange(exchange, this)); break;
      case PUT:  handlePUT(new CoapExchange(exchange, this)); break;
      case DELETE: handleDELETE(new CoapExchange(exchange, this)); break;
    }
  }
 
  /**
   * Handles the GET request in the given CoAPExchange. By default it
   * responds with a 4.05 (Method Not Allowed). Override this method to
   * respond differently to GET requests.
   *
   * @param exchange the CoapExchange for the simple API
   */
  public void handleGET(CoapExchange exchange) {
    exchange.respond(ResponseCode.METHOD_NOT_ALLOWED);
  }
 
  /**
   * Handles the POST request in the given CoAPExchange. By default it
   * responds with a 4.05 (Method Not Allowed). Override this method to
   * respond differently to POST requests.
   *
   * @param exchange the CoapExchange for the simple API
   */
  public void handlePOST(CoapExchange exchange) {
    exchange.respond(ResponseCode.METHOD_NOT_ALLOWED);
  }
 
  /**
   * Handles the PUT request in the given CoAPExchange. By default it
   * responds with a 4.05 (Method Not Allowed). Override this method to
   * respond differently to PUT requests.
   *
   * @param exchange the CoapExchange for the simple API
   */
  public void handlePUT(CoapExchange exchange) {
    exchange.respond(ResponseCode.METHOD_NOT_ALLOWED);
  }
 
  /**
   * Handles the DELETE request in the given CoAPExchange. By default it
   * responds with a 4.05 (Method Not Allowed). Override this method to
   * respond differently to DELETE requests.
   *
   * @param exchange the CoapExchange for the simple API
   */
  public void handleDELETE(CoapExchange exchange) {
    exchange.respond(ResponseCode.METHOD_NOT_ALLOWED);
  }
 
  /**
   * This method is used to apply resource-specific knowledge on the exchange.
   * If the request was successful, it sets the Observe option for the
   * response. It is important to use the notificationOrderer of the resource
   * here. Further down the layer, race conditions could cause local
   * reordering of notifications. If the response has an error code, no
   * observe relation can be established and if there was one previously it is
   * canceled. When this resource allows to be observed by clients and the
   * request is a GET request with an observe option, the
   * {@link ServerMessageDeliverer} already created the relation, as it
   * manages the observing endpoints globally.
   *
   * @param exchange the exchange
   * @param response the response
   */
  public void checkObserveRelation(Exchange exchange, Response response) {
    /*
     * If the request for the specified exchange tries to establish an observer
     * relation, then the ServerMessageDeliverer must have created such a relation
     * and added to the exchange. Otherwise, there is no such relation.
     * Remember that different paths might lead to this resource.
     */
   
    ObserveRelation relation = exchange.getRelation();
    if (relation == null) return; // because request did not try to establish a relation
   
    if (CoAP.ResponseCode.isSuccess(response.getCode())) {
      response.getOptions().setObserve(notificationOrderer.getCurrent());
     
      if (!relation.isEstablished()) {
        LOGGER.info("Successfully established observe relation between "+relation.getSource()+" and resource "+getURI());
        relation.setEstablished(true);
        addObserveRelation(relation);
      } else if (observeType != null) {
        // The resource can control the message type of the notification
        response.setType(observeType);
      }
    } // ObserveLayer takes care of the else case
  }
 
  /**
   * Creates a {@link CoapClient} that uses the same executor as this resource
   * and one of the endpoints that this resource belongs to. If no executor is
   * defined by this resource or any parent, the client will not have an
   * executor (it still works). If this resource is not yet added to a server
   * or the server has no endpoints, the client has no specific endpoint and
   * will use Californium's default endpoint.
   *
   * @return the CoAP client
   */
  public CoapClient createClient() {
    CoapClient client = new CoapClient();
    client.setExecutor(getExecutor());
    List<Endpoint> endpoints = getEndpoints();
    if (!endpoints.isEmpty())
      client.setEndpoint(endpoints.get(0));
    return client;
  }
 
  /**
   * Creates a {@link CoapClient} that uses the same executor as this resource
   * and one of the endpoints that this resource belongs to. If no executor is
   * defined by this resource or any parent, the client will not have an
   * executor (it still works). If this resource is not yet added to a server
   * or the server has no endpoints, the client has no specific endpoint and
   * will use Californium's default endpoint.
   *
   * @param uri the uri
   * @return the CoAP client
   */
  public CoapClient createClient(URI uri) {
    return createClient().setURI(uri.toString());
  }
 
  /**
   * Creates a {@link CoapClient} that uses the same executor as this resource
   * and one of the endpoints that this resource belongs to. If no executor is
   * defined by this resource or any parent, the client will not have an
   * executor (it still works). If this resource is not yet added to a server
   * or the server has no endpoints, the client has no specific endpoint and
   * will use Californium's default endpoint.
   *
   * @param uri the URI string
   * @return the CoAP client
   */
  public CoapClient createClient(String uri) {
    return createClient().setURI(uri);
  }
 
  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#add(ch.ethz.inf.vs.californium.server.resources.Resource)
   */
  @Override
  public synchronized void add(Resource child) {
    if (child.getName() == null)
      throw new NullPointerException("Child must have a name");
    if (child.getParent() != null)
      child.getParent().remove(child);
    children.put(child.getName(), child);
    child.setParent(this);
    for (ResourceObserver obs:observers)
      obs.addedChild(child);
  }
 
  /**
   * Adds the specified resource as child. This method is syntactic sugar to
   * have a fluent-interface when adding resources to a tree. For instance,
   * consider the following example:
   *
   * <pre>
   * server.add(
   *   new ResourceBase("foo")
   *     .add(new ResourceBase("a")
   *       .add(new ResourceBase("a1"))
   *       .add(new ResourceBase("a2"))
   *       .add(new ResourceBase("a3"))
   *       .add(new ResourceBase("a4"))
   *     )
   *     .add(new ResourceBase("b")
   *       .add(new ResourceBase("b1")
   *     )
   *   )
   * );
   * </pre>
   *
   * @param child the child
   * @return this
   */
  public synchronized ResourceBase add(ResourceBase child) {
    add( (Resource) child);
    return this;
  }
 
  /**
   * Adds the specified resource as child. This method is syntactic sugar to
   * have a fluent-interface when adding resources to a tree. For instance,
   * consider the following example:
   *
   * <pre>
   * server.add(
   *   new ResourceBase("foo").add(
   *     new ResourceBase("a").add(
   *       new ResourceBase("a1"),
   *       new ResourceBase("a2"),
   *       new ResourceBase("a3"),
   *       new ResourceBase("a4")
   *     ),
   *     new ResourceBase("b").add(
   *       new ResourceBase("b1")
   *     )
   *   )
   * );
   * </pre>
   *
   * @param children the child(ren)
   * @return this
   */
  public synchronized ResourceBase add(ResourceBase... children) {
    for (ResourceBase child:children)
      add(child);
    return this;
  }
 
  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#remove(ch.ethz.inf.vs.californium.server.resources.Resource)
   */
  @Override
  public synchronized boolean remove(Resource child) {
    Resource removed = remove(child.getName());
    if (removed == child) {
      child.setParent(null);
      child.setPath(null);
      for (ResourceObserver obs : observers)
        obs.removedChild(child);
      return true;
    }
    return false;
  }
 
  /**
   * Removes the child with the specified name and returns it. If no child
   * with the specified name is found, the return value is null.
   *
   * @param name the name
   * @return the removed resource or null
   */
  public synchronized Resource remove(String name) {
    return children.remove(name);
  }
 
  /**
   * Delete this resource from its parents and notify all observing CoAP
   * clients that this resource is no longer accessible.
   */
  public synchronized void delete() {
    Resource parent = getParent();
    if (parent != null) {
      parent.remove(this);
    }
   
    if (isObservable()) {
      clearAndNotifyObserveRelations(ResponseCode.NOT_FOUND);
    }
  }
 
  /**
   * Remove all observe relations to CoAP clients and notify them that the
   * observe relation has been canceled.
   */
  public void clearAndNotifyObserveRelations(ResponseCode code) {
    /*
     * draft-ietf-core-observe-08, chapter 3.2 Notification states:
     * In the event that the resource changes in a way that would cause
     * a normal GET request at that time to return a non-2.xx response
     * (for example, when the resource is deleted), the server sends a
     * notification with a matching response code and removes the client
     * from the list of observers.
     * This method is called, when the resource is deleted.
     */
    for (ObserveRelation relation:observeRelations) {
      relation.cancel();
      relation.getExchange().sendResponse(new Response(code));
    }
  }
 
  /**
   * Cancel all observe relations to CoAP clients.
   */
  public void clearObserveRelations() {
    for (ObserveRelation relation:observeRelations) {
      relation.cancel();
    }
  }
 
  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#getParent()
   */
  @Override
  public Resource getParent() {
    return parent;
  }
 
  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#setParent(ch.ethz.inf.vs.californium.server.resources.Resource)
   */
  public void setParent(Resource parent) {
    this.parent = parent;
    if (parent != null)
      this.path = parent.getPath()  + parent.getName() + "/";
    adjustChildrenPath();
  }
 
  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#getChild(java.lang.String)
   */
  @Override
  public Resource getChild(String name) {
    return children.get(name);
  }

  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#addObserver(ch.ethz.inf.vs.californium.server.resources.ResourceObserver)
   */
  @Override
  public synchronized void addObserver(ResourceObserver observer) {
    observers.add(observer);
  }

  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#removeObserver(ch.ethz.inf.vs.californium.server.resources.ResourceObserver)
   */
  @Override
  public synchronized void removeObserver(ResourceObserver observer) {
    observers.remove(observer);
  }

  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#getAttributes()
   */
  @Override
  public ResourceAttributes getAttributes() {
    return attributes;
  }
 
  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#getName()
   */
  @Override
  public String getName() {
    return name;
  }

  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#isCachable()
   */
  @Override
  public boolean isCachable() {
    return true;
  }

  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#getPath()
   */
  @Override
  public String getPath() {
    return path;
  }

  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#getURI()
   */
  @Override
  public String getURI() {
    return getPath() + getName();
  }

  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#setPath(java.lang.String)
   */
  public synchronized void setPath(String path) {
    String old = this.path;
    this.path = path;
    for (ResourceObserver obs:observers)
      obs.changedPath(old);
    adjustChildrenPath();
  }

  // If the parent already has a child with that name, the behavior is undefined
  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#setName(java.lang.String)
   */
  public synchronized void setName(String name) {
    if (name == null)
      throw new NullPointerException();
    String old = this.name;
    Resource parent = getParent();
    synchronized (parent) {
      parent.remove(this);
      this.name = name;
      parent.add(this);
    }
    for (ResourceObserver obs:observers)
      obs.changedName(old);
    adjustChildrenPath();
  }
 
  /**
   * Adjust the path of all children. This method is invoked when the URI of
   * this resource has changed, e.g., if its name or the name of an ancestor
   * has changed.
   */
  private void adjustChildrenPath() {
    String childpath = path + name + /*since 23.7.2013*/ "/";
    for (Resource child:children.values())
      child.setPath(childpath);
  }
 
  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#isVisible()
   */
  @Override
  public boolean isVisible() {
    return visible;
  }
 
  /**
   * Marks this resource as visible to CoAP clients.
   *
   * @param visible true if visible
   */
  public void setVisible(boolean visible) {
    this.visible = visible;
  }
 
  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#isObservable()
   */
  @Override
  public boolean isObservable() {
    return observable;
  }

  /**
   * Marks this resource as observable by CoAP clients.
   *
   * @param observable true if observable
   */
  public void setObservable(boolean observable) {
    this.observable = observable;
  }
 
  /**
   * Sets the type of the notifications that will be sent.
   * If set to null (default) the type matching the request will be used.
   *
   * @param type either CON, NON, or null for no changes by the framework
   * @throws IllegalArgumentException if illegal types for notifications are passed
   */
  public void setObserveType(Type type) {
    if (type == Type.ACK || type == Type.RST) throw new IllegalArgumentException("Only CON and NON notifications are allowed or null for no changes by the framework");
    this.observeType = type;
  }

  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#addObserveRelation(ch.ethz.inf.vs.californium.observe.ObserveRelation)
   */
  @Override
  public void addObserveRelation(ObserveRelation relation) {
    observeRelations.add(relation);
    for (ResourceObserver obs:observers)
      obs.addedObserveRelation(relation);
  }

  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#removeObserveRelation(ch.ethz.inf.vs.californium.observe.ObserveRelation)
   */
  @Override
  public void removeObserveRelation(ObserveRelation relation) {
    observeRelations.remove(relation);
    for (ResourceObserver obs:observers)
      obs.removedObserveRelation(relation);
  }
 
  /**
   * Returns the number of observe realtions that this resource has to CoAP
   * clients.
   *
   * @return the observer count
   */
  public int getObserverCount() {
    return observeRelations.getSize();
  }
 
  /**
   * Notifies all CoAP clients that have established an observe relation with
   * this resource that the state has changed by reprocessing their original
   * request that has established the relation. The notification is done by
   * the executor of this resource or on the executor of its parent or
   * transitively ancestor. If no ancestor defines its own executor, the
   * thread that has called this method performs the notification.
   */
  public void changed() {
    Executor executor = getExecutor();
    if (executor != null) {
      executor.execute(new Runnable() {
        public void run() {
          notifyObserverRelations();
        }
      });
    } else {
      notifyObserverRelations();
    }
  }
 
  /**
   * Notifies all CoAP clients that have established an observe relation with
   * this resource that the state has changed by reprocessing their original
   * request that has established the relation.
   */
  protected void notifyObserverRelations() {
    notificationOrderer.getNextObserveNumber();
    for (ObserveRelation relation:observeRelations) {
      relation.notifyObservers();
    }
  }

  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#getChildren()
   */
  @Override // should be used for read-only
  public Collection<Resource> getChildren() {
    return children.values();
  }
 
  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#getExecutor()
   */
  public Executor getExecutor() {
    return parent != null ? parent.getExecutor() : null;
  }
 
  /**
   * Execute an arbitrary task on the executor of this resource or the first
   * parent that defines its own executor. If no parent defines an executor,
   * the thread that calls this method executes the specified task.
   *
   * @param task the task
   */
  public void execute(Runnable task) {
    Executor executor = getExecutor();
    if (executor != null)
      executor.execute(task);
    else task.run();
  }
 
  /**
   * Execute an arbitrary task on the executor of this resource or the first
   * parent that defines its own executor and wait until it the task is
   * completed. If no parent defines an executor, the thread that calls this
   * method executes the specified task.
   *
   * @param task the task
   * @throws InterruptedException the interrupted exception
   */
  public void executeAndWait(final Runnable task) throws InterruptedException {
    final Semaphore semaphore = new Semaphore(0);
    execute(new Runnable() {
      public void run() {
        task.run();
        semaphore.release();
      }
    });
    semaphore.acquire();
  }
 
  /* (non-Javadoc)
   * @see ch.ethz.inf.vs.californium.server.resources.Resource#getEndpoints()
   */
  public List<Endpoint> getEndpoints() {
    if (parent == null)
      return Collections.emptyList();
    else return parent.getEndpoints();
  }
}
TOP

Related Classes of ch.ethz.inf.vs.californium.server.resources.ResourceBase

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.