Merge branch 'release/5.18.0'
[taxeditor.git] / eu.etaxonomy.taxeditor.store / src / main / java / eu / etaxonomy / taxeditor / store / CdmStore.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9
10 package eu.etaxonomy.taxeditor.store;
11
12 import java.util.EnumSet;
13
14 import org.eclipse.core.runtime.IProgressMonitor;
15 import org.eclipse.core.runtime.jobs.Job;
16 import org.eclipse.swt.widgets.Display;
17 import org.slf4j.Marker;
18 import org.springframework.core.io.ClassPathResource;
19 import org.springframework.core.io.Resource;
20 import org.springframework.security.authentication.AuthenticationManager;
21 import org.springframework.security.core.Authentication;
22 import org.springframework.security.core.GrantedAuthority;
23 import org.springframework.security.core.context.SecurityContext;
24 import org.springframework.security.core.context.SecurityContextHolder;
25
26 import eu.etaxonomy.cdm.api.application.CdmApplicationException;
27 import eu.etaxonomy.cdm.api.application.CdmApplicationRemoteController;
28 import eu.etaxonomy.cdm.api.application.CdmApplicationState;
29 import eu.etaxonomy.cdm.api.application.ICdmRepository;
30 import eu.etaxonomy.cdm.api.cache.CdmServiceCacher;
31 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
32 import eu.etaxonomy.cdm.api.service.IAgentService;
33 import eu.etaxonomy.cdm.api.service.IAnnotationService;
34 import eu.etaxonomy.cdm.api.service.IClassificationService;
35 import eu.etaxonomy.cdm.api.service.ICollectionService;
36 import eu.etaxonomy.cdm.api.service.ICommonService;
37 import eu.etaxonomy.cdm.api.service.IDescriptionService;
38 import eu.etaxonomy.cdm.api.service.IDescriptiveDataSetService;
39 import eu.etaxonomy.cdm.api.service.IEntityConstraintViolationService;
40 import eu.etaxonomy.cdm.api.service.IEntityValidationService;
41 import eu.etaxonomy.cdm.api.service.IGrantedAuthorityService;
42 import eu.etaxonomy.cdm.api.service.IGroupService;
43 import eu.etaxonomy.cdm.api.service.IMarkerService;
44 import eu.etaxonomy.cdm.api.service.IMediaService;
45 import eu.etaxonomy.cdm.api.service.INameService;
46 import eu.etaxonomy.cdm.api.service.IOccurrenceService;
47 import eu.etaxonomy.cdm.api.service.IPolytomousKeyNodeService;
48 import eu.etaxonomy.cdm.api.service.IPolytomousKeyService;
49 import eu.etaxonomy.cdm.api.service.IReferenceService;
50 import eu.etaxonomy.cdm.api.service.IRegistrationService;
51 import eu.etaxonomy.cdm.api.service.IRightsService;
52 import eu.etaxonomy.cdm.api.service.IService;
53 import eu.etaxonomy.cdm.api.service.ITaxonService;
54 import eu.etaxonomy.cdm.api.service.ITermNodeService;
55 import eu.etaxonomy.cdm.api.service.ITermService;
56 import eu.etaxonomy.cdm.api.service.ITermTreeService;
57 import eu.etaxonomy.cdm.api.service.IUserService;
58 import eu.etaxonomy.cdm.api.service.IVocabularyService;
59 import eu.etaxonomy.cdm.api.service.molecular.IAmplificationService;
60 import eu.etaxonomy.cdm.api.service.molecular.IPrimerService;
61 import eu.etaxonomy.cdm.cache.CdmRemoteCacheManager;
62 import eu.etaxonomy.cdm.config.ICdmSource;
63 import eu.etaxonomy.cdm.database.DbSchemaValidation;
64 import eu.etaxonomy.cdm.model.agent.AgentBase;
65 import eu.etaxonomy.cdm.model.common.Annotation;
66 import eu.etaxonomy.cdm.model.common.CdmBase;
67 import eu.etaxonomy.cdm.model.common.Language;
68 import eu.etaxonomy.cdm.model.description.DescriptionBase;
69 import eu.etaxonomy.cdm.model.description.DescriptiveDataSet;
70 import eu.etaxonomy.cdm.model.description.PolytomousKey;
71 import eu.etaxonomy.cdm.model.description.PolytomousKeyNode;
72 import eu.etaxonomy.cdm.model.media.Media;
73 import eu.etaxonomy.cdm.model.media.Rights;
74 import eu.etaxonomy.cdm.model.molecular.Amplification;
75 import eu.etaxonomy.cdm.model.molecular.Primer;
76 import eu.etaxonomy.cdm.model.name.Registration;
77 import eu.etaxonomy.cdm.model.name.TaxonName;
78 import eu.etaxonomy.cdm.model.occurrence.Collection;
79 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
80 import eu.etaxonomy.cdm.model.permission.CRUD;
81 import eu.etaxonomy.cdm.model.permission.Group;
82 import eu.etaxonomy.cdm.model.reference.Reference;
83 import eu.etaxonomy.cdm.model.taxon.Classification;
84 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
85 import eu.etaxonomy.cdm.model.term.DefinedTermBase;
86 import eu.etaxonomy.cdm.model.term.TermNode;
87 import eu.etaxonomy.cdm.model.term.TermTree;
88 import eu.etaxonomy.cdm.model.term.TermVocabulary;
89 import eu.etaxonomy.cdm.model.validation.EntityConstraintViolation;
90 import eu.etaxonomy.cdm.model.validation.EntityValidation;
91 import eu.etaxonomy.cdm.persistence.permission.ICdmPermissionEvaluator;
92 import eu.etaxonomy.cdm.persistence.permission.Role;
93 import eu.etaxonomy.taxeditor.io.ExportManager;
94 import eu.etaxonomy.taxeditor.io.ImportManager;
95 import eu.etaxonomy.taxeditor.model.MessagingUtils;
96 import eu.etaxonomy.taxeditor.preference.PreferencesUtil;
97 import eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager;
98 import eu.etaxonomy.taxeditor.session.mock.MockCdmEntitySessionManager;
99 import eu.etaxonomy.taxeditor.store.internal.TaxeditorStorePlugin;
100 import eu.etaxonomy.taxeditor.ui.dialog.RemotingLoginDialog;
101 import eu.etaxonomy.taxeditor.util.ProgressMonitorClientManager;
102
103 /**
104 * This implementation of ICdmDataRepository depends on hibernate sessions to
105 * store the data correctly for the current session. No state is held in this
106 * class.
107 *
108 * Only methods that either get or manipulate data are exposed here. So this
109 * class acts as a facade for the methods in cdmlib-service.
110 *
111 * @author n.hoffmann
112 * @created 17.03.2009
113 */
114 public class CdmStore {
115
116 public static final Resource DEFAULT_APPLICATION_CONTEXT = new ClassPathResource(
117 "/eu/etaxonomy/cdm/editorApplicationContext.xml",
118 TaxeditorStorePlugin.class);
119
120 public static final DbSchemaValidation DEFAULT_DB_SCHEMA_VALIDATION = DbSchemaValidation.VALIDATE;
121
122 protected static CdmStore instance;
123
124 private static ContextManager contextManager = new ContextManager();
125
126 private static LoginManager loginManager = new LoginManager();
127
128 private static TermManager termManager = new TermManager();
129
130 private static SearchManager searchManager = new SearchManager();
131
132 private static UseObjectStore useObjectInitializer = new UseObjectStore();
133
134 private static ProgressMonitorClientManager progressMonitorClientManager = new ProgressMonitorClientManager();
135
136 private static CdmStoreConnector job;
137
138 // private static CdmServer managedServer;
139
140 private Language language;
141
142 private ICdmSource cdmSource;
143
144 private boolean isConnected;
145
146 protected static CdmStore getDefault(boolean connecting){
147 if (instance != null && instance.isConnected()) {
148 return instance;
149 } else{// if (instance == null || !instance.isConnected) {
150 if (connecting){
151 MessagingUtils.dataSourceNotAvailableWarningDialog(instance);
152 }else{
153 MessagingUtils.noDataSourceWarningDialog(instance);
154 }
155 return null;
156 }
157 }
158
159 protected static CdmStore getDefault(){
160 return getDefault(false);
161 }
162
163 /**
164 * Initialize the with the last edited datasource
165 * @deprecated this method is not needed anymore after fully changing to remoting.
166 * Therefore it will be removed
167 */
168 // @Deprecated
169 // public static void connect() {
170 // try {
171 // ICdmSource cdmSource = CdmDataSourceRepository.getCurrentCdmSource();
172 // connect(cdmSource);
173 // } catch (Exception e) {
174 // MessagingUtils.messageDialog("Connection to CDM Source Failed", CdmStore.class, "Could not connect to target CDM Source", e);
175 // }
176 // }
177
178 /**
179 * Initialize with a specific datasource
180 *
181 * @param datasource
182 * a {@link eu.etaxonomy.cdm.database.ICdmDataSource} object.
183 */
184 public static void connect(ICdmSource cdmSource) {
185 connect(cdmSource, DEFAULT_DB_SCHEMA_VALIDATION,
186 DEFAULT_APPLICATION_CONTEXT);
187 }
188
189 public static void connect(ICdmSource cdmSource, RemotingLoginDialog loginDialog) {
190 connect(cdmSource,
191 DEFAULT_DB_SCHEMA_VALIDATION,
192 DEFAULT_APPLICATION_CONTEXT,
193 loginDialog);
194 }
195
196 /**
197 * Initialize and provide
198 *
199 * @param datasource
200 * @param dbSchemaValidation
201 * @param applicationContextBean
202 */
203 private static void connect(final ICdmSource cdmSource,
204 final DbSchemaValidation dbSchemaValidation,
205 final Resource applicationContextBean) {
206
207 MessagingUtils.info("Connecting to datasource: " + cdmSource);
208
209 job = new CdmStoreConnector(Display.getDefault(), cdmSource,
210 dbSchemaValidation, applicationContextBean);
211 job.setUser(true);
212 job.setPriority(Job.BUILD);
213 job.schedule();
214 }
215
216 private static void connect(final ICdmSource cdmSource,
217 final DbSchemaValidation dbSchemaValidation,
218 final Resource applicationContextBean,
219 RemotingLoginDialog remotingLoginDialog) {
220 RemotingLoginDialog loginDialog = remotingLoginDialog;
221 if(isActive()) {
222 // before we connect we clear the entity caches and the sessions
223 CdmRemoteCacheManager.removeEntityCaches();
224 if(getCurrentSessionManager(true) != null) {
225 getCurrentSessionManager(true).disposeAll();
226 }
227 }
228 MessagingUtils.info("Connecting to datasource: " + cdmSource);
229
230 job = new CdmStoreConnector(Display.getDefault(),
231 cdmSource,
232 dbSchemaValidation,
233 applicationContextBean);
234 job.start(loginDialog);
235
236 }
237
238 public static boolean isConnecting() {
239 return job != null && job.getState() == Job.RUNNING;
240 }
241
242 /**
243 * Closes the current application context
244 *
245 * @param monitor
246 * a {@link org.eclipse.core.runtime.IProgressMonitor} object.
247 */
248 public static void close(final IProgressMonitor monitor) {
249 Display.getDefault().asyncExec(new Runnable() {
250
251 @Override
252 public void run() {
253 getContextManager().notifyContextAboutToStop(monitor);
254 if ((monitor == null || (!monitor.isCanceled()) && isActive())) {
255 getContextManager().notifyContextStop(monitor);
256 instance.close();
257 }
258 }
259 });
260 }
261
262 public static void close(IProgressMonitor monitor, boolean async) {
263 if(async) {
264 close(monitor);
265 } else {
266 getContextManager().notifyContextAboutToStop(monitor);
267 if ((monitor == null || (!monitor.isCanceled()) && isActive())) {
268 getContextManager().notifyContextStop(monitor);
269 instance.close();
270 }
271 }
272 }
273
274 private void close() {
275 this.setConnected(false);
276 this.cdmSource = null;
277 CdmApplicationState.dispose();
278 }
279
280 public static void setInstance(ICdmRepository applicationController,
281 ICdmSource cdmSource) {
282 instance = new CdmStore(applicationController, cdmSource);
283 CdmApplicationState.setCdmServiceCacher(new CdmServiceCacher());
284 }
285
286 protected CdmStore(ICdmRepository repository,
287 ICdmSource cdmSource) {
288 CdmApplicationState.setCurrentAppConfig(repository);
289 CdmApplicationState.setCurrentDataChangeService(new CdmUIDataChangeService());
290 this.cdmSource = cdmSource;
291 setConnected(true);
292 }
293
294 /**
295 * All calls to the datastore require
296 *
297 * @return
298 */
299 private ICdmRepository getApplicationConfiguration() {
300 try {
301 return CdmApplicationState.getCurrentAppConfig();
302 } catch (Exception e) {
303 MessagingUtils.error(CdmStore.class, e);
304 }
305 return null;
306 }
307
308 public static ICdmRepository getCurrentApplicationConfiguration() {
309 if (getDefault() != null) {
310 return getDefault().getApplicationConfiguration();
311 }
312 return null;
313 }
314
315 /*
316 * CONVERSATIONS
317 */
318
319 /**
320 * Creates a new conversation, binds resources to the conversation and start
321 * a transaction for this conversation.
322 *
323 * @return a {@link eu.etaxonomy.cdm.api.conversation.ConversationHolder}
324 * object.
325 */
326 public static ConversationHolder createConversation() {
327 ConversationHolder conversation = getCurrentApplicationConfiguration()
328 .NewConversation();
329 try {
330 conversation.startTransaction();
331 }catch(Exception e){
332 MessagingUtils.messageDialog("No database connection", CdmStore.class, "No database connection available", e);
333 }
334 return conversation;
335 }
336
337 //FIXME:Remoting should be removed after moving completely to remoting
338 private MockCdmEntitySessionManager mockCdmEntitySessionManager;
339
340 private ICdmEntitySessionManager getSessionManager() {
341 //FIXME:Remoting we should only have CdmApplicationRemoteConfiguration after move to remoting
342 // bad hack which should be finally removed
343 if(getCurrentApplicationConfiguration() instanceof CdmApplicationRemoteController) {
344 return ((CdmApplicationRemoteController)getCurrentApplicationConfiguration()).getCdmEntitySessionManager();
345 } else {
346 if(mockCdmEntitySessionManager == null) {
347 mockCdmEntitySessionManager = new MockCdmEntitySessionManager();
348 }
349 return mockCdmEntitySessionManager;
350 }
351 }
352 public static ICdmEntitySessionManager getCurrentSessionManager() {
353 return getCurrentSessionManager(false);
354 }
355
356 public static ICdmEntitySessionManager getCurrentSessionManager(boolean connecting) {
357 if (getDefault(connecting) != null) {
358 return getDefault(connecting).getSessionManager();
359 }
360 return null;
361 }
362
363 /**
364 * Generic method that will scan the getters of {@link ICdmRepository} for the given service
365 * interface. If a matching getter is found the according service implementation is returned by
366 * invoking the getter otherwise the method returns <code>null</code>.
367 *
368 * @param <T>
369 * @param serviceClass
370 * @return the configured implementation of <code>serviceClass</code> or <code>null</code>
371 */
372 public static <T extends IService> T getService(Class<T> serviceClass) {
373 T service = null;
374 try {
375 service = CdmApplicationState.getService(serviceClass);
376 } catch (CdmApplicationException cae) {
377 MessagingUtils.error(CdmStore.class, cae);
378 }
379
380 return service;
381 }
382
383 /**
384 * @see #getService(Class)
385 * As ICommonService is not extending IService we need a specific request here
386 */
387 public static ICommonService getCommonService() {
388 return CdmApplicationState.getCommonService();
389 }
390
391 public static AuthenticationManager getAuthenticationManager() {
392 return getCurrentApplicationConfiguration().getAuthenticationManager();
393 }
394
395 public static ICdmPermissionEvaluator getPermissionEvaluator() {
396 return getCurrentApplicationConfiguration().getPermissionEvaluator();
397 }
398
399 /*
400 * SECURITY RELATED CONVENIENCE METHODS
401 */
402
403 /**
404 * @see org.springframework.security.access.PermissionEvaluator#hasPermission(org.springframework.security.core.Authentication, java.lang.Object, java.lang.Object)
405 *
406 * @param targetDomainObject
407 * @param permission
408 * @return
409 */
410 public static boolean currentAuthentiationHasPermission(CdmBase targetDomainObject, EnumSet<CRUD> permission){
411 //TODO use getCurrentApplicationConfiguration().currentAuthentiationHasPermission(CdmBase targetDomainObject, Operation permission) instead
412 SecurityContext context = SecurityContextHolder.getContext();
413 boolean hasPermission = false;
414 try {
415 hasPermission = getPermissionEvaluator().hasPermission(context.getAuthentication(), targetDomainObject,
416 permission);
417 } catch (org.springframework.security.access.AccessDeniedException e) {
418 /* IGNORE */
419 }
420 return hasPermission;
421 }
422
423 /**
424 * @see org.springframework.security.access.PermissionEvaluator#hasPermission(org.springframework.security.core.Authentication, java.lang.Object, java.lang.Object)
425 *
426 * @param targetDomainObject
427 * @param permission
428 * @return
429 */
430 public static boolean currentAuthentiationHasPermission(Class<? extends CdmBase> targetType, EnumSet<CRUD> permission){
431 boolean hasPermission = false;
432 try {
433 hasPermission = getPermissionEvaluator().hasPermission(getCurrentAuthentiation(), null, targetType.getName(), permission);
434 } catch (org.springframework.security.access.AccessDeniedException e) {
435 /* IGNORE */
436 }
437 return hasPermission;
438 }
439
440 public static boolean currentAuthentiationHasOneOfRoles(Role ... roles){
441 boolean hasPermission = false;
442 try {
443 hasPermission = getPermissionEvaluator().hasOneOfRoles(getCurrentAuthentiation(), roles);
444 } catch (org.springframework.security.access.AccessDeniedException e) {
445 /* IGNORE */
446 }
447 return hasPermission;
448 }
449
450 public static Authentication getCurrentAuthentiation() {
451 SecurityContext context = SecurityContextHolder.getContext();
452 return context.getAuthentication();
453 }
454
455 /*
456 * LANGUAGE
457 */
458
459 /**
460 * Provides access to the global default language set in the application preferences.
461 *
462 * @return a {@link eu.etaxonomy.cdm.model.common.Language} object.
463 */
464 public static Language getDefaultLanguage() {
465 if (getDefault().getLanguage() == null) {
466 getDefault().setLanguage(PreferencesUtil.getGlobalLanguage());
467 }
468 return getDefault().getLanguage();
469 }
470
471 public static void setDefaultLanguage(Language language) {
472 getDefault().setLanguage(language);
473 }
474
475 private Language getLanguage() {
476 return language;
477 }
478
479 private void setLanguage(Language language) {
480 this.language = language;
481 }
482
483 /*
484 * LOGIN
485 */
486
487 public static LoginManager getLoginManager() {
488 return loginManager;
489 }
490
491 public static ContextManager getContextManager() {
492 return contextManager;
493 }
494
495 public static TermManager getTermManager() {
496 return termManager;
497 }
498
499 public static SearchManager getSearchManager() {
500 return searchManager;
501 }
502
503
504 public static ProgressMonitorClientManager getProgressMonitorClientManager() {
505 return progressMonitorClientManager;
506 }
507
508 /*
509 * IMPORT/EXPORT FACTORIES
510 */
511
512 public static ImportManager getImportManager() {
513 return ImportManager.NewInstance(getCurrentApplicationConfiguration());
514 }
515
516 public static ExportManager getExportManager() {
517 return ExportManager.NewInstance(getCurrentApplicationConfiguration());
518 }
519
520 /**
521 * Whether this CdmStore is currently connected to a datasource
522 *
523 * @return a boolean.
524 */
525 public static boolean isActive() {
526 return instance != null && instance.isConnected();
527 }
528
529 public static ICdmSource getActiveCdmSource() {
530 if (isActive()) {
531 return instance.getCdmSource();
532 }
533 return null;
534 }
535
536 private ICdmSource getCdmSource() {
537 return cdmSource;
538 }
539
540 @SuppressWarnings("unchecked")
541 public static <T extends CdmBase> IService<T> getService(T cdmBase){
542 IService<T> service = null;
543 if(cdmBase!=null){
544 //get corresponding service
545 if(cdmBase.isInstanceOf(Reference.class)){
546 service = (IService<T>) getService(IReferenceService.class);
547 }
548 else if (cdmBase.isInstanceOf(AgentBase.class)){
549 service = (IService<T>) getService(IAgentService.class);
550 }
551 else if (cdmBase instanceof TaxonName) {
552 service = (IService<T>) getService(INameService.class);
553 }
554 else if (cdmBase instanceof TaxonBase) {
555 service = (IService<T>) getService(ITaxonService.class);
556 }
557 else if (cdmBase instanceof SpecimenOrObservationBase) {
558 service = (IService<T>) getService(IOccurrenceService.class);
559 }
560 else if (cdmBase instanceof Media) {
561 service = (IService<T>) getService(IMediaService.class);
562 }
563 else if (cdmBase instanceof Collection) {
564 service = (IService<T>) getService(ICollectionService.class);
565 }
566 else if (cdmBase instanceof eu.etaxonomy.cdm.model.permission.User) {
567 service = (IService<T>) getService(IUserService.class);
568 }
569 else if (cdmBase instanceof Group) {
570 service = (IService<T>) getService(IGroupService.class);
571 }
572 else if (cdmBase instanceof DescriptiveDataSet) {
573 service = (IService<T>) getService(IDescriptiveDataSetService.class);
574 }
575 else if (cdmBase instanceof TermVocabulary<?>) {
576 service = (IService<T>) getService(IVocabularyService.class);
577 }
578 else if (cdmBase instanceof DefinedTermBase<?>) {
579 service = (IService<T>) getService(ITermService.class);
580 }
581 else if (cdmBase instanceof Primer) {
582 service = (IService<T>) getService(IPrimerService.class);
583 }
584 else if (cdmBase instanceof Amplification) {
585 service = (IService<T>) getService(IAmplificationService.class);
586 }
587 else if (cdmBase instanceof PolytomousKey) {
588 service = (IService<T>) getService(IPolytomousKeyService.class);
589 }
590 else if (cdmBase instanceof PolytomousKeyNode) {
591 service = (IService<T>) getService(IPolytomousKeyNodeService.class);
592 }
593 else if (cdmBase instanceof Annotation) {
594 service = (IService<T>) getService(IAnnotationService.class);
595 }
596 else if (cdmBase instanceof Classification) {
597 service = (IService<T>) getService(IClassificationService.class);
598 }
599 else if (cdmBase instanceof DescriptionBase<?>) {
600 service = (IService<T>) getService(IDescriptionService.class);
601 }
602 else if (cdmBase instanceof EntityConstraintViolation) {
603 service = (IService<T>) getService(IEntityConstraintViolationService.class);
604 }
605 else if (cdmBase instanceof EntityValidation) {
606 service = (IService<T>) getService(IEntityValidationService.class);
607 }
608 else if (cdmBase instanceof TermNode) {
609 service = (IService<T>) getService(ITermNodeService.class);
610 }
611 else if (cdmBase instanceof TermTree) {
612 service = (IService<T>) getService(ITermTreeService.class);
613 }
614 else if (cdmBase instanceof GrantedAuthority) {
615 service = (IService<T>) getService(IGrantedAuthorityService.class);
616 }
617 else if (cdmBase instanceof Marker) {
618 service = (IService<T>) getService(IMarkerService.class);
619 }
620 else if (cdmBase instanceof Registration) {
621 service = (IService<T>) getService(IRegistrationService.class);
622 }
623 else if (cdmBase instanceof Rights) {
624 service = (IService<T>) getService(IRightsService.class);
625 }
626 else{
627 String message = "Service for entity of class %s not yet implemented in TaxEditor";
628 message = String.format(message, cdmBase.getClass().getSimpleName());
629 throw new RuntimeException(message);
630 }
631 }
632 return service;
633 }
634
635 //
636 // public static void setManagedServer(ICdmServer server) {
637 // managedServer = server;
638 // }
639 //
640 // public static ICdmServer getManagedServer() {
641 // return managedServer;
642
643 public boolean isConnected() {
644 return isConnected;
645 }
646
647 public void setConnected(boolean isConnected) {
648 this.isConnected = isConnected;
649
650 }
651
652
653 }