[Service] Handle exception if workerData is empty
[platform/framework/web/wrtjs.git] / wrt_app / service / service_runner.ts
1 import '../common/init';
2 import * as XWalkExtension from '../common/wrt_xwalk_extension';
3 import { DeviceAPIRouter } from './device_api_router';
4 import { isMainThread, parentPort, workerData } from 'worker_threads';
5 import { wrt } from '../browser/wrt';
6
7 Object.defineProperty(global, 'serviceType', {
8   value: wrt.getServiceModel(),
9   writable: false
10 });
11
12 function isServiceApplication() {
13   return global['serviceType'] !== 'UI';
14 }
15
16 function isGlobalService() {
17   return global['serviceType'] === 'GLOBAL';
18 }
19 function printAppControlData(id: string)  {
20   let reqAppControl = global.tizen.application.getCurrentApplication().getRequestedAppControl();
21   if (reqAppControl) {
22     console.debug(`id: ${id}, appControlData operation: ${reqAppControl.appControl.operation}`);
23     let appControlData = reqAppControl.appControl.data;
24     for (let dataIndex in appControlData) {
25       for (let valueIndex in appControlData[dataIndex].value)
26         console.debug(`data[${dataIndex}][${valueIndex}]: ${appControlData[dataIndex].value[valueIndex]}`);
27     }
28   }
29 }
30
31 function registerExtensionResolver(id: string) {
32   if (wrt.tv) {
33     let extensionResolver = (module: any, file_path: string) => {
34       console.debug(`resolved path: ${file_path}`);
35       let content = (wrt.tv as NativeWRTjs.TVExtension).decryptFile(id, file_path);
36       if (content) {
37         // Remove BOM
38         if (content.charCodeAt(0) === 0xFEFF)
39           content = content.slice(1);
40         module._compile(content, file_path);
41       }
42     };
43     require.extensions['.js.spm'] = extensionResolver;
44     require.extensions['.spm'] = extensionResolver;
45   }
46 }
47
48 let requestStopService = (id: string) => {
49   requestStopService = (id: string) => {};
50   setTimeout(() => wrt.stopService(id), 500);
51 }
52
53 let app: any = null;
54 let dummyTimer: any;
55 let periodLauncherAlive = 20; // 2s
56
57 let checkLauncherAlive = (id: string) => {
58   periodLauncherAlive--;
59   if (!periodLauncherAlive) {
60     periodLauncherAlive = 20;
61     if (!wrt.checkLauncherAlive(id)) {
62       console.debug(`${id} launcher was killed.`)
63       requestStopService(id);
64       checkLauncherAlive = () => {};
65     }
66   }
67 }
68
69 export function start(id: string, filename: string) {
70   let ids = id.split(':');
71   let serviceId = ids[0];
72   let packageId = wrt.getPackageId(id);
73   wrt.setServiceAppId(id);
74   wrt.security?.dropThreadPrivilege(packageId, serviceId);
75
76   XWalkExtension.initialize();
77   XWalkExtension.setRuntimeMessageHandler((type, data) => {
78     if (type === 'tizen://exit') {
79       console.debug(`${id} will be closed by ${type}`);
80       requestStopService(id);
81     }
82   });
83
84   console.debug(`serviceType : ${global['serviceType']}`)
85   new DeviceAPIRouter(id, isGlobalService());
86   printAppControlData(id);
87
88   // This is for awaking up uv loop.
89   if (isGlobalService()) {
90     dummyTimer = setInterval(() => {
91       checkLauncherAlive(id);
92     }, 100);
93   }
94
95   if (isServiceApplication()) {
96     registerExtensionResolver(id);
97     filename = wrt.getStartServiceFile(id);
98     console.debug(`start global service file: ${filename}`);
99   }
100
101   try {
102     app = require(filename);
103     if (app.onStart !== undefined) {
104       app.onStart();
105     }
106     if (app.onRequest !== undefined) {
107       app.onRequest();
108     }
109   } catch (e) {
110     console.debug(`exception on start: ${e}`);
111     requestStopService(id);
112   } finally {
113     if (isGlobalService()) {
114       wrt.finishStartingService(id);
115     }
116   }
117 }
118
119 export function stop(id: string) {
120   if (dummyTimer)
121     clearInterval(dummyTimer);
122   try {
123     if (app.onStop !== undefined) {
124       app.onStop();
125     } else if (app.onExit !== undefined) {
126       app.onExit();
127     }
128   } catch (e) {
129     console.debug(`exception on stop: ${e}`);
130   }
131 }
132
133 function run() {
134   let id = workerData.id;
135   if (!id) {
136     console.debug('workerData.id is empty!');
137     process.exit();
138   }
139
140   Object.defineProperty(global, 'internalId', {
141     value: id,
142     writable: false
143   });
144
145   // FIXME: this should be 'wrt.tv?.serviceMount(id)' after Tizen 6.5 release
146   (wrt.tv as any)?.serviceMount(id);
147
148   let filename = workerData.filename;
149   start(id, filename);
150
151   if (!parentPort)
152     return;
153   parentPort.on('message', (message) => {
154     console.debug(`Received message type : ${message.type}`);
155     if (message.type === 'wake') {
156       app?.onRequest();
157     } else if (message.type === 'stop') {
158       stop(id);
159       setTimeout(() => {
160         XWalkExtension.cleanup();
161         parentPort?.postMessage("will-terminate");
162         (wrt.tv as any)?.serviceUmount(id);
163       }, message.delay);
164     }
165   });
166 }
167
168 if (!isMainThread) {
169   run();
170 }