b286f695b55bb10a83cd5c88e799c42377c1f28e
[ccsdk/features.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP : ccsdk features
4  * ================================================================================
5  * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property.
6  * All rights reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  *
21  */
22 package org.onap.ccsdk.features.sdnr.wt.dataprovider.impl;
23
24 import java.io.IOException;
25 import java.io.PrintWriter;
26 import java.io.StringWriter;
27 import java.util.concurrent.TimeUnit;
28
29 import org.eclipse.jdt.annotation.NonNull;
30 import org.onap.ccsdk.features.sdnr.wt.common.configuration.ConfigurationFileRepresentation;
31 import org.onap.ccsdk.features.sdnr.wt.common.database.HtDatabaseClient;
32 import org.onap.ccsdk.features.sdnr.wt.dataprovider.data.ElasticSearchDataProvider;
33 import org.onap.ccsdk.features.sdnr.wt.dataprovider.data.MediatorServerDataProvider;
34 import org.onap.ccsdk.features.sdnr.wt.dataprovider.http.MsServlet;
35 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.DataProvider;
36 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.HtDatabaseMaintenance;
37 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.IEsConfig;
38 import org.opendaylight.mdsal.binding.api.RpcProviderService;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.CreateMaintenanceInput;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.CreateMaintenanceOutput;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.CreateMediatorServerInput;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.CreateMediatorServerOutput;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.CreateNetworkElementConnectionInput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.CreateNetworkElementConnectionOutput;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.DataProviderService;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.DeleteMaintenanceInput;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.DeleteMaintenanceOutput;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.DeleteMediatorServerInput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.DeleteMediatorServerOutput;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.DeleteNetworkElementConnectionInput;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.DeleteNetworkElementConnectionOutput;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadConnectionlogListInput;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadConnectionlogListOutput;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadEventlogListInput;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadEventlogListOutput;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadFaultcurrentListInput;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadFaultcurrentListOutput;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadFaultlogListInput;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadFaultlogListOutput;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadInventoryListInput;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadInventoryListOutput;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadMaintenanceListInput;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadMaintenanceListOutput;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadMediatorServerListInput;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadMediatorServerListOutput;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadNetworkElementConnectionListInput;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadNetworkElementConnectionListOutput;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadPmdata15mDeviceListInput;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadPmdata15mDeviceListOutput;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadPmdata15mListInput;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadPmdata15mListOutput;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadPmdata15mLtpListInput;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadPmdata15mLtpListOutput;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadPmdata24hDeviceListInput;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadPmdata24hDeviceListOutput;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadPmdata24hListInput;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadPmdata24hListOutput;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadPmdata24hLtpListInput;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadPmdata24hLtpListOutput;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadStatusInput;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.ReadStatusOutput;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.UpdateMaintenanceInput;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.UpdateMaintenanceOutput;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.UpdateMediatorServerInput;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.UpdateMediatorServerOutput;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.UpdateNetworkElementConnectionInput;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev190801.UpdateNetworkElementConnectionOutput;
88 import org.opendaylight.yangtools.concepts.Builder;
89 import org.opendaylight.yangtools.concepts.ObjectRegistration;
90 import org.opendaylight.yangtools.yang.binding.DataObject;
91 import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
92 import org.opendaylight.yangtools.yang.common.RpcResult;
93 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
94 import org.slf4j.Logger;
95 import org.slf4j.LoggerFactory;
96
97 import com.google.common.util.concurrent.ListenableFuture;
98
99 public class DataProviderServiceImpl implements DataProviderService, AutoCloseable {
100
101     private static final Logger LOG = LoggerFactory.getLogger(DataProviderServiceImpl.class);
102     public static final String CONFIGURATIONFILE = "etc/dataprovider.properties";
103     private static final long DATABASE_TIMEOUT_MS = 120*1000L;
104
105     private final ObjectRegistration<@NonNull DataProviderServiceImpl> rpcReg;
106     private final ElasticSearchDataProvider dataProvider;
107     private final ConfigurationFileRepresentation configuration;
108     private final EsConfig esConfig;
109         private final MediatorServerDataProvider mediatorServerDataProvider;
110
111     DataProviderServiceImpl(final RpcProviderService rpcProviderService,MsServlet mediatorServerServlet) throws Exception {
112         this.configuration = new ConfigurationFileRepresentation(CONFIGURATIONFILE);
113         this.esConfig = new EsConfig(configuration);
114         this.dataProvider = new ElasticSearchDataProvider(esConfig.getHosts(),esConfig.getBasicAuthUsername(),esConfig.getBasicAuthPassword());
115         this.dataProvider.waitForYellowDatabaseStatus(DATABASE_TIMEOUT_MS,TimeUnit.MILLISECONDS);
116         this.mediatorServerDataProvider = new MediatorServerDataProvider(esConfig.getHosts(),esConfig.getBasicAuthUsername(),esConfig.getBasicAuthPassword());
117         mediatorServerServlet.setDataProvider(this.mediatorServerDataProvider);
118         // Register ourselves as the REST API RPC implementation
119         LOG.info("Register RPC Service " + DataProviderServiceImpl.class.getSimpleName());
120         this.rpcReg = rpcProviderService.registerRpcImplementation(DataProviderService.class, this);
121     }
122     private void sendResyncCallbackToApiGateway() {
123         mediatorServerDataProvider.triggerReloadSync();
124     }
125     /**
126      * @return dataProvider
127      */
128     public DataProvider getDataProvider() {
129         return dataProvider.getDataProvider();
130     }
131
132     public HtDatabaseClient getRawClient() {
133         return this.dataProvider.getRawClient();
134     }
135     /**
136      * @return data provider for Maintenance()
137      */
138     public HtDatabaseMaintenance getHtDatabaseMaintenance() {
139         return dataProvider.getHtDatabaseMaintenance();
140     }
141
142     /**
143      * @return configuration object
144      */
145     public IEsConfig getEsConfig() {
146         return esConfig;
147     }
148
149
150     @Override
151     public void close() throws Exception {
152         LOG.info("Close RPC Service");
153         if (rpcReg != null) {
154             rpcReg.close();
155         }
156     }
157
158     @Override
159     public ListenableFuture<RpcResult<ReadFaultcurrentListOutput>> readFaultcurrentList(ReadFaultcurrentListInput input) {
160         LOG.debug("RPC Request: getFaultCurrentEntityList with input {}", input);
161         RpcResultBuilder<ReadFaultcurrentListOutput> result = read(()-> DataProviderServiceImpl.this.dataProvider.readFaultCurrentList(input));
162         return result.buildFuture();
163     }
164     @Override
165     public ListenableFuture<RpcResult<ReadFaultlogListOutput>> readFaultlogList(ReadFaultlogListInput input) {
166         LOG.debug("RPC Request: getFaultCurrentEntityList with input {}", input);
167         RpcResultBuilder<ReadFaultlogListOutput> result = read(()-> DataProviderServiceImpl.this.dataProvider.readFaultLogList(input));
168         return result.buildFuture();
169     }
170
171     @Override
172     public ListenableFuture<RpcResult<ReadMaintenanceListOutput>> readMaintenanceList(ReadMaintenanceListInput input) {
173         LOG.debug("RPC Request: getMaintenanceEntityList with input {}", input);
174         RpcResultBuilder<ReadMaintenanceListOutput> result = read(()-> DataProviderServiceImpl.this.dataProvider.readMaintenanceList(input));
175         return result.buildFuture();
176     }
177
178     @Override
179     public ListenableFuture<RpcResult<ReadMediatorServerListOutput>> readMediatorServerList(
180             ReadMediatorServerListInput input) {
181         LOG.debug("RPC Request: getMediatorServerEntityList with input {}", input);
182         RpcResultBuilder<ReadMediatorServerListOutput> result = read(()-> DataProviderServiceImpl.this.dataProvider.readMediatorServerList(input));
183         return result.buildFuture();
184     }
185
186     @Override
187     public ListenableFuture<RpcResult<ReadNetworkElementConnectionListOutput>> readNetworkElementConnectionList(
188             ReadNetworkElementConnectionListInput input) {
189         LOG.debug("RPC Request: getNetworkElementConnectionEntityList with input {}", input);
190         RpcResultBuilder<ReadNetworkElementConnectionListOutput> result = read(()-> DataProviderServiceImpl.this.dataProvider.readNetworkElementConnectionList(input));
191         return result.buildFuture();
192
193     }
194
195     @Override
196     public ListenableFuture<RpcResult<ReadPmdata15mListOutput>> readPmdata15mList(ReadPmdata15mListInput input) {
197         LOG.debug("RPC Request: getNetworkElementConnectionEntityList with input {}", input);
198         RpcResultBuilder<ReadPmdata15mListOutput> result = read(()-> DataProviderServiceImpl.this.dataProvider.readPmdata15mList(input));
199         return result.buildFuture();
200
201     }
202     @Override
203     public ListenableFuture<RpcResult<ReadPmdata24hListOutput>> readPmdata24hList(ReadPmdata24hListInput input) {
204         LOG.debug("RPC Request: getNetworkElementConnectionEntityList with input {}", input);
205         RpcResultBuilder<ReadPmdata24hListOutput> result = read(()-> DataProviderServiceImpl.this.dataProvider.readPmdata24hList(input));
206         return result.buildFuture();
207
208     }
209     @Override
210     public ListenableFuture<RpcResult<ReadStatusOutput>> readStatus(ReadStatusInput input) {
211         LOG.debug("RPC Request: getStatusEntityList with input {}", input);
212         RpcResultBuilder<ReadStatusOutput> result = read(()-> DataProviderServiceImpl.this.dataProvider.readStatus());
213         return result.buildFuture();
214
215     }
216
217     @Override
218     public ListenableFuture<RpcResult<ReadInventoryListOutput>> readInventoryList(ReadInventoryListInput input) {
219         LOG.debug("RPC Request: getInventoryEntityList with input {}", input);
220         RpcResultBuilder<ReadInventoryListOutput> result = read(()-> DataProviderServiceImpl.this.dataProvider.readInventoryList(input));
221         return result.buildFuture();
222     }
223
224     @Override
225     public ListenableFuture<RpcResult<ReadPmdata15mLtpListOutput>> readPmdata15mLtpList(ReadPmdata15mLtpListInput input) {
226         LOG.debug("RPC Request: readPmdataLtpList with input {}", input);
227         RpcResultBuilder<ReadPmdata15mLtpListOutput> result = read(()-> DataProviderServiceImpl.this.dataProvider.readPmdata15mLtpList(input));
228         return result.buildFuture();
229     }
230
231     @Override
232     public ListenableFuture<RpcResult<ReadPmdata15mDeviceListOutput>> readPmdata15mDeviceList(ReadPmdata15mDeviceListInput input) {
233             LOG.debug("RPC Request: readPmdataDeviceList with input {}", input);
234             RpcResultBuilder<ReadPmdata15mDeviceListOutput> result = read(()-> DataProviderServiceImpl.this.dataProvider.readPmdata15mDeviceList(input));
235             return result.buildFuture();
236     }
237
238     @Override
239     public ListenableFuture<RpcResult<ReadPmdata24hLtpListOutput>> readPmdata24hLtpList(ReadPmdata24hLtpListInput input) {
240         LOG.debug("RPC Request: readPmdataLtpList with input {}", input);
241         RpcResultBuilder<ReadPmdata24hLtpListOutput> result = read(()-> DataProviderServiceImpl.this.dataProvider.readPmdata24hLtpList(input));
242         return result.buildFuture();
243     }
244
245     @Override
246     public ListenableFuture<RpcResult<ReadPmdata24hDeviceListOutput>> readPmdata24hDeviceList(ReadPmdata24hDeviceListInput input) {
247             LOG.debug("RPC Request: readPmdataDeviceList with input {}", input);
248             RpcResultBuilder<ReadPmdata24hDeviceListOutput> result = read(()-> DataProviderServiceImpl.this.dataProvider.readPmdata24hDeviceList(input));
249             return result.buildFuture();
250     }
251
252     @Override
253     public ListenableFuture<RpcResult<ReadConnectionlogListOutput>> readConnectionlogList(
254             ReadConnectionlogListInput input) {
255         LOG.debug("RPC Request: readConnectionlogList with input {}", input);
256         RpcResultBuilder<ReadConnectionlogListOutput> result = read(() -> DataProviderServiceImpl.this.dataProvider.readConnectionlogList(input));
257         return result.buildFuture();
258     }
259
260     @Override
261     public ListenableFuture<RpcResult<CreateNetworkElementConnectionOutput>> createNetworkElementConnection(
262             CreateNetworkElementConnectionInput input) {
263         LOG.debug("RPC Request: createNetworkElementConnection with input {}", input);
264         RpcResultBuilder<CreateNetworkElementConnectionOutput> result = read(() -> DataProviderServiceImpl.this.dataProvider.createNetworkElementConnection(input));
265         return result.buildFuture();
266     }
267
268     @Override
269     public ListenableFuture<RpcResult<UpdateNetworkElementConnectionOutput>> updateNetworkElementConnection(
270             UpdateNetworkElementConnectionInput input) {
271         LOG.debug("RPC Request: updateNetworkElementConnection with input {}", input);
272         RpcResultBuilder<UpdateNetworkElementConnectionOutput> result = read(() -> DataProviderServiceImpl.this.dataProvider.updateNetworkElementConnection(input));
273         return result.buildFuture();
274     }
275
276     @Override
277     public ListenableFuture<RpcResult<DeleteNetworkElementConnectionOutput>> deleteNetworkElementConnection(
278             DeleteNetworkElementConnectionInput input) {
279         RpcResultBuilder<DeleteNetworkElementConnectionOutput> result = read(() -> DataProviderServiceImpl.this.dataProvider.deleteNetworkElementConnection(input));
280         return result.buildFuture();
281     }
282
283     @Override
284     public ListenableFuture<RpcResult<DeleteMediatorServerOutput>> deleteMediatorServer(
285             DeleteMediatorServerInput input) {
286         LOG.debug("RPC Request: deleteMediatorServer with input {}", input);
287         RpcResultBuilder<DeleteMediatorServerOutput> result = read(() -> DataProviderServiceImpl.this.dataProvider.deleteMediatorServer(input));
288         this.sendResyncCallbackToApiGateway();
289         return result.buildFuture();
290     }
291
292     @Override
293     public ListenableFuture<RpcResult<CreateMediatorServerOutput>> createMediatorServer(
294             CreateMediatorServerInput input) {
295         LOG.debug("RPC Request: createMediatorServer with input {}", input);
296         RpcResultBuilder<CreateMediatorServerOutput> result = read(() -> DataProviderServiceImpl.this.dataProvider.createMediatorServer(input));
297         this.sendResyncCallbackToApiGateway();
298         return result.buildFuture();
299     }
300
301     @Override
302     public ListenableFuture<RpcResult<CreateMaintenanceOutput>> createMaintenance(CreateMaintenanceInput input) {
303         LOG.debug("RPC Request: createMaintenance with input {}", input);
304         RpcResultBuilder<CreateMaintenanceOutput> result = read(() -> DataProviderServiceImpl.this.dataProvider.createMaintenance(input));
305         return result.buildFuture();
306     }
307
308     @Override
309     public ListenableFuture<RpcResult<DeleteMaintenanceOutput>> deleteMaintenance(DeleteMaintenanceInput input) {
310         LOG.debug("RPC Request: deleteMaintenance with input {}", input);
311         RpcResultBuilder<DeleteMaintenanceOutput> result = read(() -> DataProviderServiceImpl.this.dataProvider.deleteMaintenance(input));
312         return result.buildFuture();
313     }
314
315     @Override
316     public ListenableFuture<RpcResult<UpdateMediatorServerOutput>> updateMediatorServer(
317             UpdateMediatorServerInput input) {
318         LOG.debug("RPC Request: updateMediatorServer with input {}", input);
319         RpcResultBuilder<UpdateMediatorServerOutput> result = read(() -> DataProviderServiceImpl.this.dataProvider.updateMediatorServer(input));
320         this.sendResyncCallbackToApiGateway();
321         return result.buildFuture();
322     }
323
324     @Override
325     public ListenableFuture<RpcResult<UpdateMaintenanceOutput>> updateMaintenance(UpdateMaintenanceInput input) {
326         LOG.debug("RPC Request: updateMaintenance with input {}", input);
327         RpcResultBuilder<UpdateMaintenanceOutput> result = read(() -> DataProviderServiceImpl.this.dataProvider.updateMaintenance(input));
328         return result.buildFuture();
329     }
330
331     @Override
332     public ListenableFuture<RpcResult<ReadEventlogListOutput>> readEventlogList(ReadEventlogListInput input) {
333         LOG.debug("RPC Request: readEventlogList with input {}", input);
334         RpcResultBuilder<ReadEventlogListOutput> result = read(() -> DataProviderServiceImpl.this.dataProvider.readEventlogList(input));
335         return result.buildFuture();
336     }
337
338     // -- private classes and functions
339
340     private static String assembleExceptionMessage(Exception e) {
341         StringWriter sw = new StringWriter();
342         PrintWriter pw = new PrintWriter(sw);
343         e.printStackTrace(pw);
344
345         StringBuffer buf = new StringBuffer();
346         buf.append("Exception: ");
347         buf.append(sw.toString());
348         return buf.toString();
349     }
350
351     private interface GetEntityInput<O extends DataObject> {
352         Builder<O> get() throws IOException;
353     }
354
355     private static <O extends DataObject,I extends DataObject> RpcResultBuilder<O> read(GetEntityInput<O> inputgetter ) {
356         RpcResultBuilder<O> result;
357         try {
358             Builder<O> outputBuilder = inputgetter.get();
359             result = RpcResultBuilder.success(outputBuilder);
360         } catch (Exception e) {
361             LOG.info("Exception", e);
362             result = RpcResultBuilder.failed();
363             result.withError(ErrorType.APPLICATION, assembleExceptionMessage(e));
364         }
365         return result;
366     }
367
368 }