Update according to design review
[platform/core/csapi/tizenfx.git] / src / Tizen.Network.IoTConnectivity / Tizen.Network.IoTConnectivity / Resource.cs
1  /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
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
18 using System;
19 using System.Collections.Generic;
20 using System.Collections.ObjectModel;
21 using System.Runtime.InteropServices;
22
23 namespace Tizen.Network.IoTConnectivity
24 {
25     /// <summary>
26     /// Abstract class respresenting a resource.
27     /// All resources need to inherit from this class.
28     /// </summary>
29     public abstract class Resource : IDisposable
30     {
31         private IntPtr _resourceHandle = IntPtr.Zero;
32         private bool _disposed = false;
33         private ObservableCollection<Resource> _children = new ObservableCollection<Resource>();
34         private IntPtr _observerHandle = IntPtr.Zero;
35
36         /// <summary>
37         /// The constructor
38         /// </summary>
39         /// <remarks>
40         /// @a uri format would be relative URI path like '/a/light'
41         /// and its length must be less than 128.
42         /// </remarks>
43         /// <privilege>
44         /// http://tizen.org/privilege/internet
45         /// </privilege>
46         /// <param name="uri">The URI path of the resource</param>
47         /// <param name="types">Resource types</param>
48         /// <param name="interfaces">Resource interfaces</param>
49         /// <param name="policy">The policies of the resoruce</param>
50         /// <pre>
51         /// IoTConnectivityServerManager.Initialize() should be called to initialize
52         /// </pre>
53         /// <seealso cref="ResourceTypes"/>
54         /// <seealso cref="ResourceInterfaces"/>
55         /// <seealso cref="ResourcePolicy"/>
56         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported</exception>
57         /// <exception cref="OutOfMemoryException">Thrown when there is not enough memory</exception>
58         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter</exception>
59         /// <code>
60         /// // Create a class which inherits from Resource
61         /// public class DoorResource : Resource
62         /// {
63         ///     public DoorResource(string uri, ResourceTypes types, ResourceInterfaces interfaces, ResourcePolicy policy)
64         ///             : base(uri, types, interfaces, policy) {
65         ///     }
66         ///     protected override Response OnDelete(Request request) {
67         ///         // Do something
68         ///     }
69         ///     protected override Response OnGet(Request request) {
70         ///         // Do something
71         ///     }
72         ///     // Override other abstract methods of Resource class
73         /// }
74         ///
75         /// // Use it like below
76         /// ResourceInterfaces ifaces = new ResourceInterfaces(new List<string>(){ ResourceInterfaces.DefaultInterface });
77         /// ResourceTypes types = new ResourceTypes(new List<string>(){ "oic.iot.door.new" });
78         /// Resource resource = new DoorResource("/door/uri1", types, ifaces, ResourcePolicy.Discoverable | ResourcePolicy.Observable);
79         /// </code>
80         protected Resource(string uri, ResourceTypes types, ResourceInterfaces interfaces, ResourcePolicy policy)
81         {
82             UriPath = uri;
83             Types = types;
84             Interfaces = interfaces;
85             Policy = policy;
86
87             _children.CollectionChanged += ChildrenCollectionChanged;
88
89             int ret = Interop.IoTConnectivity.Server.Observers.Create(out _observerHandle);
90             if (ret != (int)IoTConnectivityError.None)
91             {
92                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to create obsever handle");
93                 throw IoTConnectivityErrorFactory.GetException(ret);
94             }
95         }
96
97         /// <summary>
98         /// Destructor of the Resource class.
99         /// </summary>
100         ~Resource()
101         {
102             Dispose(false);
103         }
104
105         /// <summary>
106         /// Type details of the resource
107         /// </summary>
108         public ResourceTypes Types { get; internal set; }
109
110         /// <summary>
111         /// Interface details of the resource
112         /// </summary>
113         public ResourceInterfaces Interfaces { get; internal set; }
114
115         /// <summary>
116         /// The policies of the resource
117         /// </summary>
118         public ResourcePolicy Policy { get; internal set; }
119
120         /// <summary>
121         /// URI path of the resource
122         /// </summary>
123         public string UriPath { get; internal set; }
124
125         /// <summary>
126         /// List of Child resources
127         /// </summary>
128         public ICollection<Resource> Children
129         {
130             get
131             {
132                 return _children;
133             }
134         }
135
136         internal IntPtr ResourceHandle
137         {
138             get
139             {
140                 return _resourceHandle;
141             }
142             set
143             {
144                 _resourceHandle = value;
145             }
146         }
147
148         /// <summary>
149         /// Notify the specified representation and qos.
150         /// </summary>
151         /// <privilege>
152         /// http://tizen.org/privilege/internet
153         /// </privilege>
154         /// <param name="representation">Representation.</param>
155         /// <param name="qos">The quality of service for message transfer.</param>
156         /// <pre>
157         /// IoTConnectivityServerManager.Initialize() should be called to initialize
158         /// </pre>
159         /// <seealso cref="Representation"/>
160         /// <seealso cref="QualityOfService"/>
161         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported</exception>
162         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter</exception>
163         /// <exception cref="UnauthorizedAccessException">Thrown when app does not have privilege to access</exception>
164         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid</exception>
165         /// <code>
166         /// ResourceInterfaces ifaces = new ResourceInterfaces(new List<string>(){ ResourceInterfaces.DefaultInterface });
167         /// ResourceTypes types = new ResourceTypes(new List<string>(){ "oic.iot.door.new.notify" });
168         /// Resource resource = new DoorResource("/door/uri/new/notify", types, ifaces, ResourcePolicy.Discoverable | ResourcePolicy.Observable);
169         /// IoTConnectivityServerManager.RegisterResource(resource);
170         ///
171         /// Representation repr = new Representation();
172         /// repr.UriPath = "/door/uri/new/notify";
173         /// repr.Type = new ResourceTypes(new List<string>(){ "oic.iot.door.new.notify" });
174         /// repr.Attributes = new Attributes() {
175         ///      _attribute, 1 }
176         /// };
177         /// resource.Notify(repr, QualityOfService.High);
178         /// </code>
179         public void Notify(Representation representation, QualityOfService qos)
180         {
181             int ret = (int)IoTConnectivityError.None;
182             ret = Interop.IoTConnectivity.Server.Resource.Notify(_resourceHandle, representation._representationHandle, _observerHandle, (int)qos);
183             if (ret != (int)IoTConnectivityError.None)
184             {
185                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to send notification");
186                 throw IoTConnectivityErrorFactory.GetException(ret);
187             }
188         }
189
190         /// <summary>
191         /// This is Called when the client performs get operation on this resource.
192         /// </summary>
193         /// <param name="request">A request from client</param>
194         /// <returns>A response having the representation and the result</returns>
195         protected abstract Response OnGet(Request request);
196
197         /// <summary>
198         /// This is Called when the client performs put operation on this resource.
199         /// </summary>
200         /// <param name="request">A request from client</param>
201         /// <returns>A response</returns>
202         protected abstract Response OnPut(Request request);
203
204         /// <summary>
205         /// This is Called when the client performs post operation on this resource.
206         /// </summary>
207         /// <param name="request">A request from client</param>
208         /// <returns>A response having the representation and the result</returns>
209         protected abstract Response OnPost(Request request);
210
211         /// <summary>
212         /// This is Called when the client performs delete operation on this resource.
213         /// </summary>
214         /// <param name="request">A request from client</param>
215         /// <returns>A response</returns>
216         protected abstract Response OnDelete(Request request);
217
218         /// <summary>
219         /// Called on the observing event.
220         /// </summary>
221         /// <param name="request">A request from client</param>
222         /// <param name="type">Observer type</param>
223         /// <param name="observeId">Observe identifier.</param>
224         /// <returns>Returns true if it wants to be observed, else false.</returns>
225         protected abstract bool OnObserving(Request request, ObserveType type, int observeId);
226
227         /// <summary>
228         /// Releases any unmanaged resources used by this object.
229         /// </summary>
230         public void Dispose()
231         {
232             Dispose(true);
233             GC.SuppressFinalize(this);
234         }
235
236         /// <summary>
237         /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
238         /// </summary>
239         /// <param name="disposing">If true, disposes any disposable objects. If false, does not dispose disposable objects.</param>
240         protected virtual void Dispose(bool disposing)
241         {
242             if (_disposed)
243                 return;
244
245             if (disposing)
246             {
247                 Types?.Dispose();
248                 Interfaces?.Dispose();
249             }
250
251             if (_resourceHandle != IntPtr.Zero)
252                 Interop.IoTConnectivity.Server.Resource.Destroy(_resourceHandle);
253             if (_observerHandle != IntPtr.Zero)
254                 Interop.IoTConnectivity.Server.Observers.Destroy(_observerHandle);
255             _disposed = true;
256         }
257
258         // This method is used as callback for Resource
259         internal void OnRequest(IntPtr resourceHandle, IntPtr requestHandle, IntPtr userData)
260         {
261             Request request = GetRequest(requestHandle);
262             Response response = null;
263
264             if (request == null)
265             {
266                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get Request");
267                 return;
268             }
269
270             try
271             {
272                 int observeType;
273                 int ret = Interop.IoTConnectivity.Server.Request.GetObserveType(requestHandle, out observeType);
274                 if (ret != (int)IoTConnectivityError.None)
275                 {
276                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to Get observe type");
277                     return;
278                 }
279                 if ((ObserveType)observeType != ObserveType.NoType)
280                 {
281                     int observeId;
282                     ret = Interop.IoTConnectivity.Server.Request.GetObserveId(requestHandle, out observeId);
283                     if (ret != (int)IoTConnectivityError.None)
284                     {
285                         Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to Get observe id");
286                         return;
287                     }
288                     switch ((ObserveType)observeType)
289                     {
290                         case ObserveType.Register:
291                         {
292                             if (OnObserving(request, ObserveType.Register, observeId))
293                             {
294                                 ret = Interop.IoTConnectivity.Server.Observers.Add(_observerHandle, observeId);
295                                 if (ret != (int)IoTConnectivityError.None)
296                                 {
297                                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to add observer id");
298                                     return;
299                                 }
300                                 break;
301                             }
302                             else
303                             {
304                                 // If OnObserving for ObserveType.Register returns false, do not operate for Get operation after Observe operation.
305                                 return;
306                             }
307                         }
308                         case ObserveType.Deregister:
309                         {
310                             if (OnObserving(request, ObserveType.Deregister, observeId))
311                             {
312                                 ret = Interop.IoTConnectivity.Server.Observers.Remove(_observerHandle, observeId);
313                                 if (ret != (int)IoTConnectivityError.None)
314                                 {
315                                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to remove observer id");
316                                     return;
317                                 }
318                                 break;
319                             }
320                             else
321                             {
322                                 // If OnObserving for ObserveType.Deregister returns false, do not operate for Get operation after Observe operation.
323                                 return;
324                             }
325                         }
326                     }
327                 }
328                 int requestType;
329                 ret = Interop.IoTConnectivity.Server.Request.GetRequestType(requestHandle, out requestType);
330                 if (ret != (int)IoTConnectivityError.None)
331                 {
332                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to Get request type");
333                     return;
334                 }
335                 switch ((Interop.IoTConnectivity.Server.RequestType)requestType)
336                 {
337                     case Interop.IoTConnectivity.Server.RequestType.Put:
338                     {
339                         response = OnPut(request);
340                         break;
341                     }
342                     case Interop.IoTConnectivity.Server.RequestType.Get:
343                     {
344                         response = OnGet(request);
345                         break;
346                     }
347                     case Interop.IoTConnectivity.Server.RequestType.Post:
348                     {
349                         response = OnPost(request);
350                         break;
351                     }
352                     case Interop.IoTConnectivity.Server.RequestType.Delete:
353                     {
354                         response = OnDelete(request);
355                         break;
356                     }
357                     default:
358                         break;
359                 }
360                 if (response == null)
361                 {
362                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to send Response");
363                     return;
364                 }
365
366                 if (!response.Send(requestHandle))
367                 {
368                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to send Response");
369                     return;
370                 }
371             }
372             finally
373             {
374                 request?.Dispose();
375                 response?.Dispose();
376             }
377         }
378
379         private Request GetRequest(IntPtr requestHandle)
380         {
381             IntPtr hostAddressPtr;
382             int ret = Interop.IoTConnectivity.Server.Request.GetHostAddress(requestHandle, out hostAddressPtr);
383             if (ret != (int)IoTConnectivityError.None)
384             {
385                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to Get host address");
386                 return null;
387             }
388
389             IntPtr optionsHandle = IntPtr.Zero;
390             ret = Interop.IoTConnectivity.Server.Request.GetOptions(requestHandle, out optionsHandle);
391             if (ret != (int)IoTConnectivityError.None)
392             {
393                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to Get options");
394                 return null;
395             }
396
397             IntPtr queryHandle = IntPtr.Zero;
398             ret = Interop.IoTConnectivity.Server.Request.GetQuery(requestHandle, out queryHandle);
399             if (ret != (int)IoTConnectivityError.None)
400             {
401                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to Get Query");
402                 return null;
403             }
404
405             IntPtr representationHandle = IntPtr.Zero;
406             ret = Interop.IoTConnectivity.Server.Request.GetRepresentation(requestHandle, out representationHandle);
407             if (ret != (int)IoTConnectivityError.None)
408             {
409                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to Get representation");
410                 return null;
411             }
412
413             ResourceOptions opts = null;
414             ResourceQuery query = null;
415             Representation representation = null;
416             try
417             {
418                 opts = (optionsHandle == IntPtr.Zero) ? null : new ResourceOptions(optionsHandle);
419             }
420             catch (Exception exp)
421             {
422                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to new ResourceOptions: " + exp.Message);
423                 return null;
424             }
425
426             try
427             {
428                 query = (queryHandle == IntPtr.Zero) ? null : new ResourceQuery(queryHandle);
429             }
430             catch (Exception exp)
431             {
432                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to new ResourceQuery: " + exp.Message);
433                 return null;
434             }
435
436             try
437             {
438                 representation = (representationHandle == IntPtr.Zero) ? null : new Representation(representationHandle); ;
439             }
440             catch (Exception exp)
441             {
442                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to new Representation: " + exp.Message);
443                 return null;
444             }
445
446             return new Request()
447             {
448                 HostAddress = Marshal.PtrToStringAnsi(hostAddressPtr),
449                 Options = opts,
450                 Query = query,
451                 Representation = representation
452             };
453         }
454
455         private void ChildrenCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs eventArgs)
456         {
457             if (eventArgs.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
458             {
459                 foreach (Resource r in eventArgs.NewItems)
460                 {
461                     int ret = Interop.IoTConnectivity.Server.Resource.BindChildResource(_resourceHandle, r._resourceHandle);
462                     if (ret != (int)IoTConnectivityError.None)
463                     {
464                         Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to bind resource ");
465                         throw IoTConnectivityErrorFactory.GetException(ret);
466                     }
467                 }
468             }
469             else if (eventArgs.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
470             {
471                 foreach (Resource r in eventArgs.NewItems)
472                 {
473                     int ret = Interop.IoTConnectivity.Server.Resource.UnbindChildResource(_resourceHandle, r._resourceHandle);
474                     if (ret != (int)IoTConnectivityError.None)
475                     {
476                         Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to unbind resource");
477                         throw IoTConnectivityErrorFactory.GetException(ret);
478                     }
479                 }
480             }
481         }
482     }
483 }