Small logo of ETH main building ETH Zurich : Computer Science : Pervasive Computing : Distributed Systems : People : Matthias Kovatsch : Californium : Docs

Projects Home

Californium – A CoAP framework in Java

This HTML documentation is based on the lab thesis by Daniel Pauli and Dominique Im Obersteg. While continuing the development of Californium, I will update this page over time.

Ideas behind Californium

In the following few paragraphs, we would like to point out the fundamental ideas behind Californium that heavily influenced our design decisions.

Abstraction

Californium aims to shield users from the details of CoAP in order to provide an intuitive, easy-to-use framework to interact with CoAP endpoints or provide specific services. Following that, users do not need to deal with internals like message retransmissions, block-wise transfers and observation handling. From a client's view, a RESTful operation can be performed by just creating a GET, POST, PUT, or DELETE request object, specifying the URI of the target endpoint, followed by payload and other options if required. After execution of the request, the resulting responses can be processed either synchronously or asynchronously. Setting up a CoAP server using Californium just requires to define its resources by providing subclasses that implement the GET, POST, PUT, and/or DELETE request handlers. Thus, no custom message dispatching is required. Nevertheless, protocol components and properties can still be configured to suit extended requirements.

Modularity

First, Californium is based on a layered architecture, extending the two-layer approach described in the CoAP draft. This allows an isolated implementation of different aspects such as message retransmission, transactions, and block-wise transfers specified by CoAP over several levels. Second, message communication is decoupled from the state representation of CoAP endpoints. Although currently not implemented, this design allows the modeling of endpoints similar to remote objects of Java's RMI system. Hence, a client could access resources on a remote endpoint over a local stub, as if it were an object on the local machine.

Maintainability

As described in Section 2.2, our implementation is highly modular and therefore also easy to maintain. Future adaptions and changes can be implemented by only changing the corresponding part of the code without going through the hassle of reading thousands of lines of code and changing the architecture. Protocol constants that may be subject to change in later CoAP versions as well as Californium specific constants, are managed over an easy-accessible object containing library specific parameters that can be synchronized with a configuration file to allow convenient adjustments by users.

Extensibility

A direct consequence of the modularity is the augmented extensibility of our CoAP library. Communication-related features can be added by introducing new layers in the communication stack, which is convenient due to the uniform interfaces of the layers. Adding a new layer provides the possibility to include and exclude certain components for testing purposes. Client- and server-related features can be implemented by extending the Endpoint classes, e.g., to provide proxying support.

Backwards Compatibility

Californium implements CoAP draft 08. To ensure that our implementation of CoAP still works with servers and clients based on previous drafts, we also provide a support for reasonable properties of previous drafts, such as option numbers and return codes.

Architectural Design

Figure 3.1 provides an overview over the whole system architecture. The following sections will provide more details about the packages and the classes therein contained.

Class Diagram Figure 3.1: Class Diagram

Packages

Package 'ch.ethz.inf.vs.californium.coap'

Package 'coap' Figure 3.2: Package 'ch.ethz.inf.vs.californium.coap'

This package contains all classes that provide the functionality of the core Californium CoAP library. It includes entity classes defining CoAP messages and the derived request and response subclasses as well as static classes containing enumerations and constants for the different registries specified by the IETF CoAP draft. Furthermore, it defines common interfaces that are used to communicate between packages.

Interfaces

MessageHandler
The interface MessageHandler describes the method signatures to handle a request or response. It is mainly used by the communication layer classes for the double dispatch to process messages according to their subtype.
MessageReceiver
The interface MessageReceiver describes the method signature to receive a message. It is mainly used by communication layer and endpoint classes to implement the propagation of incoming messages.
RequestHandler
The interface RequestHandler describes the method signatures to handle requests (GET, POST, PUT, and DELETE). This interface is implemented by Resource classes and users will implement its methods in order to define custom resource behavior of Californium servers.
ResponseHandler
The interface ResponseHandler describes the method signature to handle a response. It is used to propagate responses to requests over the affected endpoints back to the communication layer classes.

Classes

CodeRegistry
The class CodeRegistry provides the CoAP code registry as defined in the draft. It contains constants for request and response codes as well as the corresponding string representations. Users will primarily need this registry to specify or check response codes of CoAP messages.
OptionNumberRegistry
The class OptionNumberRegistry provides the CoAP option number registry as defined in the draft. It contains constants for CoAP option numbers as well as the corresponding string representations. Users will primarily need this registry to specify or check additional options of CoAP messages where no convenience accessor/mutator methods (such as Message.setContentType()) are provided.
MediaTypeRegistry
The class MediaTypeRegistry provides the CoAP media type registry as defined in the draft. It contains constants for content-type codes as well as the corresponding string representations. Users will primarily need this registry to specify the content-type for custom CoAP messages.
Communicator
The class Communicator provides the functionality to build the communication layer stack and to send and receive messages. As a subclass of UpperLayer (see Section 3.2.3) it is actually a composite layer that contains the subsequent layers in the top-down order as explained in Table 3.1.
Hence, the Communicator class is used to encapsulate the various communication layers of the CoAP protocol by providing an appropriate unified interface. Internally, it instantiates the required communication layer classes and connects them accordingly. The Communicator also acts as Mediator between endpoint classes and communication layer classes, allowing to specify and query parameters like the UDP port.
LayerDescription
Transfer
Transaction
  • Matching of responses to the according requests
  • Transaction timeouts, e.g., to limit wait time for separate responses and responses to non-confirmable requests
Message
  • Reliable transport of Confirmable messages over underlying layers by making use of retransmissions and exponential backoff
  • Matching of Confirmables to their corresponding Acknowledgment/Reset
  • Detection and cancellation of duplicate messages
  • Retransmission of Acknowledgments/Reset messages upon receiving duplicate Confirmable messages
UDP
  • UDP datagram exchange
Table 3.1: Californium communication layers
Message
The class Message represents the core entities for the message exchange between CoAP endpoints. All communication layers process objects of this class. This class provides accessor and mutator methods to the CoAP message header and payload. Internally, it implements the conversion from the Java object to the serialized datagram representation. Users will primarily use the following methods of this class:
public URI getURI()
public void setURI(URI uri)
public boolean setURI(String uri)
Accesses or modifies the URI property of a message. For outgoing messages, this is the URI of the recipient. For incoming messages, it is the URI of the sender.
public byte[] getPayload()
public String getPayloadString()
public void setPayload(byte[] payload)
public void setPayload(String payload)
public void setPayload(String payload, int mediaType)
Accesses or modifies the payload of a message, either as UTF-8 string or raw binary data. The content-type option will be specified accordingly.
public void addOption(Option opt)
public void setOption(Option opt)
public void removeOption(int optionNumber)
Adds or removes CoAP options from a message. Depending on the option, multiple options with the same option number can be added to a message. addOption() will append an option to the list of options with the same number, setOption() will replace any options with the same number and removeOption() will remove all options with the specified number.
public List
Accesses or modifies the list of CoAP options with the same option number. getFirstOption() is a convenience method to access the option in case where only one option is allowed, e.g., for the Token option. Note that users will instantiate request and response subclasses rather than directly create a new message object.
Option
The class Option provides the functionality of the CoAP options. A message can have several Options with different or same option numbers, as required by the draft. Every option is associated with a value of implicit type and hence provides accessor and mutator methods of different types to the option value. This allows clients to e.g., generate tokens as consecutive integers, while servers will treat token as opaque objects and echo them without further assumptions about their format. Users will primarily use the following methods of this class:
public Option(int val, int nr)
public Option(String str, int nr)
public Option(byte[] raw, int nr)
Constructor for a new option with the given option number. The option value can be specified either as UTF-8 string, as integer number or as raw binary value, depending on the option number. New options can be added to messages using the Message.addOption() method.
public int getIntValue()
public void setIntValue(int val)
public String getStringValue()
public void setStringValue(String str)
public byte[] getRawValue()
public void setRawValue(byte[] value)
Accesses or modifies the value of the option. The option value can be specified either as UTF-8 string, as integer number or as raw binary value, depending on the option number.
BlockOption
The class BlockOption provides the CoAP block option as a subclass of Option. It is used to encode/decode the NUM, SZX and M fields as well as derived values thereof.
Request
The class Request provides the functionality of a CoAP request as a subclass of Message. It provides different ways to answer with a matching response at the server side and to handle incoming responses at the client. Users can register an asynchronous handler using registerResponseHandler(), create a sub-class (e.g., anonymous inner class) that provides asynchronous handling by overriding the protected method handleResponse(), or call the synchronous (blocking) method receiveResponse(). In order to perform a request, users will instantiate a subclass corresponding to the desired operation:
  • GETRequest,
  • PUTRequest,
  • POSTRequest, and
  • DELETERequest.
The subclasses are required by the double dispatch mechanism for the message handling upon receival. Generally, the se the following Request methods will be required (in addition to the methods provided by the Message class):
public void execute() throws IOException
Sends the request for processing to the remote endpoint specified by the URI property on instantiation or by setURI(). This method will return immediately rather than block.
public void respond(Response response)
public void respond(int code)
public void respond(int code, String message)
Sends a response to the endpoint where the request originated from. This method is generally used in server-side request handlers in order to answer to a processed request. Note that a request can be answered by multiple responses (please refer to the 'Response' class description for more information).
public void accept()
public void reject()
Accepts or rejects a request by sending an ACK or RST to the endpoint where the request originated from. These methods are generally used in more time-consuming request handlers to acknowledge the receipt of a message immediately, avoiding retransmissions of the request. The final response is then issued using the respond() method, which will be delivered to the sender using a separate message. Note that it is not required nor suggested to always perform accept() before respond().
public void enableResponseQueue(boolean enable)
This method is used to define how incoming responses to this request are handled. If the argument is set to true, incoming responses will be queued and can be retrieved using the synchronous receiveResponse() method. If the argument is set to false, incoming responses are not stored and need to be processed asynchronously using response handlers.
public Response receiveResponse() throws InterruptedException
This method blocks until a response to this request was received. It returns null if no response can be received (e.g., when the DEFAULT_TRANSACTION_TIMEOUT occurred). In order to use this method, the response queue must be enabled (see enableResponseQueue()). This mechanism is usually used by single-threaded clients that only perform one request at a time.
public void registerResponseHandler(ResponseHandler handler)
public void unregisterResponseHandler(ResponseHandler handler)
Registers a handler object that is notified about incoming responses to this request. This mechanism is generally used by clients that implement a similar response handling regardless of specific requests, e.g., log the arrival of a response in a file.
protected void handleResponse(Response response)
This protected method is called upon receiving a response to this request. Subclasses can override this method to provide custom response handling. This mechanism is generally used by clients that implement response handling specific to requests, e.g., display the results of a resource discovery. The handler method can be implemented conveniently using anonymous inner classes.
Response
The class Response provides the functionality of a CoAP response as a subclass of Message. Requests and responses are in a one-to-many-relationship, i.e., a request may receive an arbitrary number of responses (as for separate responses, observing, and broadcasts). A response is related with exactly one request. Users will primarily use the following methods of this class (in addition to the methods provided by the Message class):
public Request getRequest()
Returns the request that is related with this response.
public int getRTT()
Returns the round trip time of this response in milliseconds. The corresponding time stamps are set in the UDP layer.

Package 'ch.ethz.inf.vs.californium.endpoint'

Package 'endpoint' Figure 3.3: Package 'ch.ethz.inf.vs.californium.endpoint'

This package contains all classes which provide the functionality for CoAP endpoints, both local and remote. It includes structural classes defining CoAP resources as well as endpoints as generalization of the client-server model. The general idea is to provide a common interface for both local endpoints (corresponding to own server or client implementations using Californium) and remote endpoints (which are only accessible in terms of CoAP message exchanges). Similar as in the Java RMI System, RemoteEndpoint/RemoteResource objects could be implemented as local stubs to LocalEndpoint/LocalResource objects on remote machines, respectively.

Abstract Classes

Endpoint
The abstract class Endpoint describes the functionality of a CoAP endpoint as an implementation of the interfaces MessageReceiver and MessageHandler. It provides an interface to execute requests on a CoAP endpoint as well as access to its resources. An Endpoint class provides a Communicator object that is used for message exchange. Additionally, it contains a single resource that represents the root of the resource tree.
Resource
The abstract class Resource describes the functionality of a CoAP resource as an implementation of the interface RequestHandler. Resources are the core entities of a CoAP endpoint, providing services that can be accessed and manipulated over RESTful operations. Hence, the main purpose of a resource is to process a request and return adequate responses. Resources can contain sub-resources, allowing for content directories or similar, and offer operations to query or iterate over a resource subtree. Finally, as resource can contain several attributes as specified by the CoRE Link Format, attributes are represented using a TreeMap in order to support generic attributes, while the resource class also provides convenient accessor and mutator methods for well-known attributes.

Classes

LocalEndpoint
The class LocalEndpoint provides the functionality of a local CoAP endpoint as a subclass of Endpoint. A client of the Californium framework will override this class in order to provide custom resources. Internally, the main purpose of this class is to forward received requests to the according resource specified by the Uri-Path option. Furthermore, it implements the root resource to return a brief server description to GET requests with empty Uri-Path.
LocalResource
The class LocalResource provides the functionality of a local CoAP resource as a subclass of Resource. Users will inherit this class in order to provide custom resources by overriding some the following methods:
public void performGET(GETRequest request)
public void performPOST(POSTRequest request)
public void performPUT(PUTRequest request)
public void performDELETE(DELETERequest request)
These methods are defined by the RequestHandler interface and have a default implementation in this class that respond with "4.05 Method Not Allowed."
DiscoveryResource
The class DiscoveryResource provides the functionality of a CoAP discovery entry point as a subclass of LocalResource. It basically implements the "/.well-known/core" resource and returns the list of resources provided by a CoAP endpoint in link format upon a GET request.
RemoteEndpoint
Not implemented
RemoteResource
Not implemented

Package 'ch.ethz.inf.vs.californium.layers'

Package 'layers' Figure 3.4: Package 'ch.ethz.inf.vs.californium.layers'

This package contains all classes which provide the functionality required in order to represent and implement the layered architecture of the Californium CoAP design.

Abstract Classes

Layer
The abstract class Layer describes a layer in the Californium architecture as an implementation of the interface MessageReceiver. It provides features to send and receive CoAP Messages and allows clients to subscribe for incoming messages, following the observer pattern. Additionally, it provides an interface for implementing communication statistics.
UpperLayer
The abstract class UpperLayer describes a higher communication layer in the Californium architecture as a subclass of Layer. Every UpperLayer is associated with a underlaying layer instance that is used to send and receive CoAP Messages. All layer classes except UDPLayer (as the base layer) inherit from UpperLayer and override the protected methods doSendMessage() and doReceiveMessage() in order to implement additional features to the communication stack. Finally, messages are propagated using methods sendMessageOverLowerLayer() and deliverMessage(), respectively.

Classes

AdverseLayer
The class AdverseLayer provides the functionality of an adverse layer as a subclass of UpperLayer. It drops messages with a given probability in order to test retransmissions between MessageLayer and UDPLayer. Used for testing and evaluation purposes only, it is not present in the final communication stack.
MessageLayer
The class MessageLayer provides the functionality of a CoAP message layer as a subclass of UpperLayer. It introduces reliable transport of confirmable messages over underlying layers by making use of retransmissions and exponential backoff, matching of confirmables to their corresponding ACK/RST, detection and cancellation of duplicate messages, retransmission of ACK/RST messages upon receiving duplicate confirmable messages.
TransactionLayer
The class TransactionLayer provides the functionality of a CoAP transaction layer as a subclass of UpperLayer. It implements the matching of responses to the corresponding requests and introduces a custom timeout for requests to complete.
TransferLayer
The class TransactionLayer provides the functionality of a CoAP transfer layer as a subclass of UpperLayer. It provides support for blockwise transfers using Block1 and Block2 options and custom block sizes and limits.
UDPLayer
The class UDPLayer provides the functionality of a UDP layer that is able to exchange CoAP messages with remote endpoints. According to the UDP protocol, messages are exchanged over an unreliable channel and thus may arrive out of order, appear duplicated or are being lost without any notice.

Package 'ch.ethz.inf.vs.californium.util'

Package 'util' Figure 3.4: Package 'ch.ethz.inf.vs.californium.util'

This package contains all classes which provide the functionality required for logging and managing preferences.

Classes

DatagramReader
The class DatagramReader provides the functionality to read raw network-ordered datagrams on bit-level. It is used to parse CoAP messages with respect to the integer fields of varying bit lengths.
DatagramWriter
The class DatagramWriter provides the functionality to write raw network-ordered datagrams on bit-level. It is used to serialize CoAP messages with respect to the integer fields of varying bit lengths.
Log
The class Log provides the functionality to log events in the CoAP library. It is used to redirect console output and provide uniform error messages.
Properties
The class Properties implements the functionality of an extended properties registry. It is used to manage CoAP- and Californium-specific constants in a central place.

Sequences

The following two sequence diagrams show the interaction of the different objects (and layers) when sending (Figure 3.6) or receiving (Figure 3.7) a message. The implementation details of the operations are split over the different communication layers.

Send

Whenever an endpoint calls execute on a request, the send order is propagated down the different layers until it reaches the UDPLayer which then sends the datagram over UDP.

Send operation Figure 3.6: Send operation

Receive

Whenever the UDPLayer receives a message (response to a request), the receive order is propagated up the different layer until it reaches the Endpoint which then handles the response.

Receive operation Figure 3.7: Receive operation

Response Timeout

Whenever the UDPLayer does not receives a response to a request, i.e., a receive time out has occurred, it repeatedly sends the request again until a response is received.

Response timeout Figure 3.8: Response timeout

Examples

In the following few paragraphs, we would like to point out the fundamental ideas behind Californium that heavily influenced our design decisions.

Synchronous Client

This example demonstrates the receiveResponse() method. This approach is generally used by single-threaded clients that only perform one single request at a time, such as console applications.

public class GETClient {

	public static void main(String args[]) {
		
		URI uri = null; // URI parameter of the request
		
		if (args.length > 0) {
			
			// input URI from command line arguments
			try {
				uri = new URI(args[0]);
			} catch (URISyntaxException e) {
				System.err.println("Invalid URI: " + e.getMessage());
				System.exit(-1);
			}
		
			// create new request
			Request request = new GETRequest();
			// specify URI of target endpoint
			request.setURI(uri);
			// enable response queue for synchronous I/O
			request.enableResponseQueue(true);
			
			// execute the request
			try {
				request.execute();
			} catch (IOException e) {
				System.err.println("Failed to execute request: " + e.getMessage());
				System.exit(-1);
			}
			
			// receive response
			try {
				Response response = request.receiveResponse();
				
				if (response != null) {
					// response received, output a pretty-print of it
					response.log();
				} else {
					// transaction timeout occurred
					System.out.println("No response received.");
				}
				
			} catch (InterruptedException e) {
				System.err.println("Receiving of response interrupted: " + e.getMessage());
				System.exit(-1);
			}
			
		} else {
			// display help
			System.out.println("Usage: GETClient URI");
		}
	}
}

Asynchronous Client with Listener

This example demonstrates how to register a client class to handle responses. This approach is generally used by clients that implement a similar response handling regardless of specific requests, e.g., log the arrival of a response in a file.

static class MyClient implements ResponseHandler {
	
	public void performSampleRequest() {
	
		// create new request
		Request request = new GETRequest();
		
		// specify URI of target endpoint
		request.setURI("coap://vs0.inf.ethz.ch:5683/timeResource");
		
		// register MyClient as response handler
		request.registerResponseHandler(this);
		
		// execute the request
		try {
			request.execute();
		} catch (IOException e) {
			System.err.println("Failed to execute request: " + e.getMessage());
			System.exit(-1);
		}
	
		/*
		 * Do something or return to the message loop of the GUI
		 */
	}

	@Override
	public void handleResponse(Response response) {
		// specific handling for this request
		// here: response received, output a pretty-print
		response.log();
	}
}

Asynchronous Client with Subclassing (here: anonymous inner class)

This example demonstrates how to use an anonymous inner class to handle responses. This approach is generally used by clients that implement response handling specific to requests, e.g. display the results of a resource discovery.

public void performSampleRequest() {
	
	// create new request using an anonymous inner class
	Request request = new GETRequest() {
		@Override
		protected void handleResponse(Response response) {
			// specific handling for this request
			// here: response received, output a pretty-print
			response.log();
		}
	};
	
	// specify URI of target endpoint
	request.setURI("coap://vs0.inf.ethz.ch:5683/timeResource");
	
	// execute the request
	try {
		request.execute();
	} catch (IOException e) {
		System.err.println("Failed to execute request: " + e.getMessage());
		System.exit(-1);
	}
}

Hello World Server

The code below demonstrates how to implement a simple CoAP server that provides a resource which returns "Hello World" upon a GET-Request.

public class HelloWorldServer extends LocalEndpoint {
	
	class HelloWorldResource extends ReadOnlyResource {

		/**
		 * Constructor for a new Hello-World resource. Here,
		 * resource-specific properties are set.
		 */
		public HelloWorldResource() {

			// call constructor of superclass and
			// provide resource identifier
			super("helloWorld"); 
			
			// set display name
			setResourceTitle("Hello-World Resource");
		}

		/**
		 * Implementation of the actual functionality by overriding
		 * the default GET request handler 
		 */
		@Override
		public void performGET(GETRequest request) {
			// respond to the request with the according response code and payload
			request.respond(CodeRegistry.RESP_CONTENT, "Hello World!");
		}
	}
	
	/**
	 * Constructor for a new Hello-World server. Here, 
	 * the resources of the server are initialized.
	 */
	public HelloWorldServer() throws SocketException {
		// provide an instance of the Hello-World resource
		addResource(new HelloWorldResource());
	}

	/*
	 * Application entry point.
	 */
	public static void main(String[] args) {
		
		try {
			// create server
			HelloWorldServer server = new HelloWorldServer();
			System.out.println("Server listening on port " + server.port());
			
		} catch (SocketException e) {
			System.err.println("Failed to initialize server: " + e.getMessage());
		}
	}
}

Storage Resource

The code below demonstrates a more complex resource for a server. It can be used to store hierarchical data like a file system. Content of the individual resources can be accessed using GET requests. New sub-resources can be created using POST, updated using PUT, and removed again using DELETE requests. The resources can also be observed.

public class StorageResource extends LocalResource {

	private byte[] data;
	
	/**
	 * Default constructor.
	 */
	public StorageResource() {
		this("storage");
	}
	
	/**
	 * Constructs a new storage resource with the given resourceIdentifier.
	 */
	public StorageResource(String resourceIdentifier) {
		super(resourceIdentifier);
		setResourceTitle("PUT your data here or POST new resources!");
		setResourceType("Storage");
		setObservable(true);
	}

	// REST Operations /////////////////////////////////////////////////////////
	
	/**
	 * GETs the content of this storage resource. 
	 * If the content-type of the request is set to application/link-format 
	 * or if the resource does not store any data, the contained sub-resources
	 * are returned in link format.
	 */
	@Override
	public void performGET(GETRequest request) {

		// create response
		Response response = new Response(CodeRegistry.RESP_CONTENT);

		// check if link format requested
		if (request.hasFormat(MediaTypeRegistry.LINK_FORMAT) || data == null) {

			// respond with list of sub-resources in link format
			response.setPayload(toLinkFormat(), MediaTypeRegistry.LINK_FORMAT);

		} else {

			// load data into payload
			response.setPayload(data);

			// set content type
			response.setContentType(getContentTypeCode());
		}

		// complete the request
		request.respond(response);
	}
	
	/**
	 * PUTs content to this resource.
	 */
	@Override
	public void performPUT(PUTRequest request) {

		// store payload
		storeData(request);

		// complete the request
		request.respond(CodeRegistry.RESP_CHANGED);
	}

	/**
	 * POSTs a new sub-resource to this resource.
	 * The name of the new sub-resource is retrieved from the request
	 * payload.
	 */
	@Override
	public void performPOST(POSTRequest request) {

		// get request payload as a string
		String payload = request.getPayloadString();
		
		// check if valid Uri-Path specified
		if (payload != null && !payload.isEmpty()) {

			createNew(request, payload);

		} else {
			// complete the request by reporting error
			request.respond(CodeRegistry.RESP_BAD_REQUEST,
				"Payload must contain Uri-Path for new sub-resource.");
		}
	}

	/**
	 * Creates a new sub-resource with the given identifier in this
	 * resource, recursively creating sub-resources along the Uri-Path
	 * if necessary.
	 */
	@Override
	public void createNew(Request request, String newIdentifier) {
		
		// omit leading and trailing slashes
		if (newIdentifier.startsWith("/")) {
			newIdentifier = newIdentifier.substring(1);
		}
		if (newIdentifier.endsWith("/")) {
			newIdentifier = newIdentifier.substring(0, newIdentifier.length()-1);
		}
		
		// check if resource should be created as a sub-resource
		// of the current (this) resource
		int delim = newIdentifier.indexOf('/');
		if (delim < 0) {

			// create new sub-resource if it does not yet exist
			StorageResource resource = (StorageResource)subResource(newIdentifier);
			if (resource == null) {
				
				// create new resource and add it to the current resource
				resource = new StorageResource(newIdentifier);
				addSubResource(resource);
		
				// store payload
				resource.storeData(request);
		
				// create new response
				Response response = new Response(CodeRegistry.RESP_CREATED);
		
				// inform client about the location of the new resource
				response.setLocationPath(resource.getResourcePath());
		
				// complete the request
				request.respond(response);
			
			} else {
				
				// resource already exists; update data
				storeData(request);
				
				// complete the request
				request.respond(CodeRegistry.RESP_CHANGED);
			}
			
		} else {
			
			// split path in parent and sub identifier
			String parentIdentifier = newIdentifier.substring(0, delim);
			newIdentifier = newIdentifier.substring(delim+1);
			
			// retrieve corresponding sub-resource, create if necessary
			StorageResource sub = (StorageResource) subResource(parentIdentifier);
			if (sub == null) {
				sub = new StorageResource(parentIdentifier);
				addSubResource(sub);
			}
			
			// delegate creation to the sub-resource
			sub.createNew(request, newIdentifier);
		}
	}
	
	/**
	 * DELETEs this storage resource, if it is not root.
	 */
	@Override
	public void performDELETE(DELETERequest request) {

		// disallow to remove the root "storage" resource
		if (parent instanceof StorageResource) {

			// remove this resource
			remove();

			request.respond(CodeRegistry.RESP_DELETED);
		} else {
			request.respond(CodeRegistry.RESP_FORBIDDEN,
				"Root storage resource cannot be deleted");
		}
	}

	// Internal ////////////////////////////////////////////////////////////////
	
	/*
	 * Convenience function to store data contained in a 
	 * PUT/POST-Request. Notifies observing endpoints about
	 * the change of its contents.
	 */
	private void storeData(Request request) {

		// set payload and content type
		data = request.getPayload();
		setContentTypeCode(request.getContentType());

		// signal that resource state changed
		changed();
	}
}
ETH ZurichDistributed Systems Group
Last updated December 6 2011 01:36:52 PM MET ko