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