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