001/* 002 * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Nuxeo - initial API and implementation 018 * 019 */ 020 021package org.nuxeo.connect.client.jsf; 022 023import java.io.IOException; 024import java.io.Serializable; 025import java.nio.file.attribute.FileTime; 026import java.text.DateFormat; 027import java.text.SimpleDateFormat; 028import java.util.ArrayList; 029import java.util.Date; 030import java.util.HashMap; 031import java.util.List; 032import java.util.Locale; 033import java.util.Map; 034import java.util.TimeZone; 035import java.util.concurrent.TimeUnit; 036import java.util.stream.Stream; 037 038import javax.faces.context.FacesContext; 039import javax.faces.model.SelectItem; 040 041import org.apache.commons.lang3.ArrayUtils; 042import org.apache.commons.logging.Log; 043import org.apache.commons.logging.LogFactory; 044import org.jboss.seam.ScopeType; 045import org.jboss.seam.annotations.In; 046import org.jboss.seam.annotations.Name; 047import org.jboss.seam.annotations.Scope; 048import org.jboss.seam.contexts.Contexts; 049import org.jboss.seam.faces.FacesMessages; 050import org.jboss.seam.international.StatusMessage; 051import org.nuxeo.common.utils.ExceptionUtils; 052import org.nuxeo.connect.client.ui.SharedPackageListingsSettings; 053import org.nuxeo.connect.client.vindoz.InstallAfterRestart; 054import org.nuxeo.connect.client.we.StudioSnapshotHelper; 055import org.nuxeo.connect.connector.ConnectServerError; 056import org.nuxeo.connect.connector.http.ConnectUrlConfig; 057import org.nuxeo.connect.data.DownloadablePackage; 058import org.nuxeo.connect.data.DownloadingPackage; 059import org.nuxeo.connect.packages.PackageManager; 060import org.nuxeo.connect.packages.dependencies.DependencyResolution; 061import org.nuxeo.connect.packages.dependencies.TargetPlatformFilterHelper; 062import org.nuxeo.connect.update.LocalPackage; 063import org.nuxeo.connect.update.PackageDependency; 064import org.nuxeo.connect.update.PackageException; 065import org.nuxeo.connect.update.PackageState; 066import org.nuxeo.connect.update.PackageType; 067import org.nuxeo.connect.update.PackageUpdateService; 068import org.nuxeo.connect.update.ValidationStatus; 069import org.nuxeo.connect.update.task.Task; 070import org.nuxeo.ecm.admin.AdminViewManager; 071import org.nuxeo.ecm.admin.runtime.PlatformVersionHelper; 072import org.nuxeo.ecm.admin.runtime.ReloadHelper; 073import org.nuxeo.ecm.admin.setup.SetupWizardActionBean; 074import org.nuxeo.ecm.core.api.NuxeoException; 075import org.nuxeo.ecm.platform.ui.web.util.ComponentUtils; 076import org.nuxeo.ecm.webapp.seam.NuxeoSeamHotReloadContextKeeper; 077import org.nuxeo.launcher.config.ConfigurationException; 078import org.nuxeo.launcher.config.ConfigurationGenerator; 079import org.nuxeo.runtime.api.Framework; 080import org.nuxeo.runtime.reload.ReloadService; 081import org.nuxeo.runtime.services.config.ConfigurationService; 082import org.nuxeo.runtime.util.Watch; 083import org.nuxeo.runtime.util.Watch.TimeInterval; 084 085/** 086 * Manages JSF views for Package Management. 087 * 088 * @author <a href="mailto:td@nuxeo.com">Thierry Delprat</a> 089 */ 090@Name("appsViews") 091@Scope(ScopeType.CONVERSATION) 092public class AppCenterViewsManager implements Serializable { 093 094 private static final long serialVersionUID = 1L; 095 096 protected static final Log log = LogFactory.getLog(AppCenterViewsManager.class); 097 098 private static final String LABEL_STUDIO_UPDATE_STATUS = "label.studio.update.status."; 099 100 /** 101 * FIXME JC: should follow or simply reuse {@link PackageState} 102 */ 103 protected enum SnapshotStatus { 104 downloading, saving, installing, error, completed, restartNeeded; 105 } 106 107 protected static final Map<String, String> view2PackageListName = new HashMap<String, String>() { 108 private static final long serialVersionUID = 1L; 109 { 110 put("ConnectAppsUpdates", "updates"); 111 put("ConnectAppsStudio", "studio"); 112 put("ConnectAppsRemote", "remote"); 113 put("ConnectAppsLocal", "local"); 114 } 115 }; 116 117 @In(create = true) 118 protected String currentAdminSubViewId; 119 120 @In(create = true) 121 protected NuxeoSeamHotReloadContextKeeper seamReloadContext; 122 123 @In(create = true) 124 protected SetupWizardActionBean setupWizardAction; 125 126 @In(create = true, required = false) 127 protected FacesMessages facesMessages; 128 129 @In(create = true) 130 protected Map<String, String> messages; 131 132 protected String searchString; 133 134 protected SnapshotStatus studioSnapshotStatus; 135 136 protected int studioSnapshotDownloadProgress; 137 138 protected boolean isStudioSnapshopUpdateInProgress = false; 139 140 protected String studioSnapshotUpdateError; 141 142 /** 143 * Boolean indicating is Studio snapshot package validation should be done. 144 * 145 * @since 5.7.1 146 */ 147 protected Boolean validateStudioSnapshot; 148 149 /** 150 * Last validation status of the Studio snapshot package 151 * 152 * @since 5.7.1 153 */ 154 protected ValidationStatus studioSnapshotValidationStatus; 155 156 private FileTime lastUpdate = null; 157 158 protected DownloadablePackage studioSnapshotPackage; 159 160 /** 161 * Using a dedicated property because studioSnapshotPackage might be null. 162 * 163 * @since 7.10 164 */ 165 protected Boolean studioSnapshotPackageCached = false; 166 167 public String getSearchString() { 168 if (searchString == null) { 169 return ""; 170 } 171 return searchString; 172 } 173 174 public void setSearchString(String searchString) { 175 this.searchString = searchString; 176 } 177 178 public boolean getOnlyRemote() { 179 return SharedPackageListingsSettings.instance().get("remote").isOnlyRemote(); 180 } 181 182 public void setOnlyRemote(boolean onlyRemote) { 183 SharedPackageListingsSettings.instance().get("remote").setOnlyRemote(onlyRemote); 184 } 185 186 protected String getListName() { 187 return view2PackageListName.get(currentAdminSubViewId); 188 } 189 190 public void setPlatformFilter(boolean doFilter) { 191 SharedPackageListingsSettings.instance().get(getListName()).setPlatformFilter(doFilter); 192 } 193 194 public boolean getPlatformFilter() { 195 return SharedPackageListingsSettings.instance().get(getListName()).getPlatformFilter(); 196 } 197 198 public String getPackageTypeFilter() { 199 return SharedPackageListingsSettings.instance().get(getListName()).getPackageTypeFilter(); 200 } 201 202 public void setPackageTypeFilter(String filter) { 203 SharedPackageListingsSettings.instance().get(getListName()).setPackageTypeFilter(filter); 204 } 205 206 public List<SelectItem> getPackageTypes() { 207 List<SelectItem> types = new ArrayList<>(); 208 SelectItem allItem = new SelectItem("", "label.packagetype.all"); 209 types.add(allItem); 210 for (PackageType ptype : PackageType.values()) { 211 // if (!ptype.equals(PackageType.STUDIO)) { 212 SelectItem item = new SelectItem(ptype.getValue(), "label.packagetype." + ptype.getValue()); 213 types.add(item); 214 // } 215 } 216 return types; 217 } 218 219 public void flushCache() { 220 PackageManager pm = Framework.getService(PackageManager.class); 221 pm.flushCache(); 222 } 223 224 /** 225 * Method binding for the update button: needs to perform a real redirection (as ajax context is broken after hot 226 * reload) and to provide an outcome so that redirection through the URL service goes ok (even if it just reset its 227 * navigation handler cache). 228 * 229 * @since 5.6 230 */ 231 public String installStudioSnapshotAndRedirect() { 232 installStudioSnapshot(); 233 return AdminViewManager.VIEW_ADMIN; 234 } 235 236 public void installStudioSnapshot() { 237 if (isStudioSnapshopUpdateInProgress) { 238 return; 239 } 240 PackageManager pm = Framework.getService(PackageManager.class); 241 // TODO NXP-16228: should directly request the SNAPSHOT package (if only we knew its name!) 242 List<DownloadablePackage> pkgs = pm.listRemoteAssociatedStudioPackages(); 243 DownloadablePackage snapshotPkg = StudioSnapshotHelper.getSnapshot(pkgs); 244 studioSnapshotUpdateError = null; 245 resetStudioSnapshotValidationStatus(); 246 if (snapshotPkg != null) { 247 isStudioSnapshopUpdateInProgress = true; 248 try { 249 StudioAutoInstaller studioAutoInstaller = new StudioAutoInstaller(pm, snapshotPkg.getId(), 250 shouldValidateStudioSnapshot()); 251 studioAutoInstaller.run(); 252 } finally { 253 isStudioSnapshopUpdateInProgress = false; 254 } 255 } else { 256 studioSnapshotUpdateError = translate("label.studio.update.error.noSnapshotPackageFound"); 257 } 258 } 259 260 public boolean isStudioSnapshopUpdateInProgress() { 261 return isStudioSnapshopUpdateInProgress; 262 } 263 264 /** 265 * Returns true if validation should be performed 266 * 267 * @since 5.7.1 268 */ 269 public Boolean getValidateStudioSnapshot() { 270 return validateStudioSnapshot; 271 } 272 273 /** 274 * @since 5.7.1 275 */ 276 public void setValidateStudioSnapshot(Boolean validateStudioSnapshot) { 277 this.validateStudioSnapshot = validateStudioSnapshot; 278 } 279 280 /** 281 * Returns true if Studio snapshot module should be validated. 282 * <p> 283 * Validation can be skipped by user, or can be globally disabled by setting framework property 284 * "studio.snapshot.disablePkgValidation" to true. 285 * 286 * @since 5.7.1 287 */ 288 protected boolean shouldValidateStudioSnapshot() { 289 ConfigurationService cs = Framework.getService(ConfigurationService.class); 290 if (cs.isBooleanPropertyTrue("studio.snapshot.disablePkgValidation")) { 291 return false; 292 } 293 return Boolean.TRUE.equals(getValidateStudioSnapshot()); 294 } 295 296 protected static String translate(String label, Object... params) { 297 return ComponentUtils.translate(FacesContext.getCurrentInstance(), label, params); 298 } 299 300 protected FileTime getLastUpdateDate() { 301 if (lastUpdate == null) { 302 DownloadablePackage snapshotPkg = getStudioProjectSnapshot(); 303 if (snapshotPkg != null) { 304 PackageUpdateService pus = Framework.getService(PackageUpdateService.class); 305 try { 306 LocalPackage pkg = pus.getPackage(snapshotPkg.getId()); 307 if (pkg != null) { 308 lastUpdate = pus.getInstallDate(pkg.getId()); 309 } 310 } catch (PackageException e) { 311 log.error(e); 312 } 313 } 314 } 315 return lastUpdate; 316 } 317 318 /** 319 * @since 7.10 320 */ 321 public String getStudioUrl() { 322 return ConnectUrlConfig.getStudioUrl(getSnapshotStudioProjectName()); 323 } 324 325 /** 326 * @since 7.10 327 */ 328 public DownloadablePackage getStudioProjectSnapshot() { 329 if (!studioSnapshotPackageCached) { 330 PackageManager pm = Framework.getService(PackageManager.class); 331 // TODO NXP-16228: should directly request the SNAPSHOT package (if only we knew its name!) 332 List<DownloadablePackage> pkgs = pm.listRemoteAssociatedStudioPackages(); 333 studioSnapshotPackage = StudioSnapshotHelper.getSnapshot(pkgs); 334 studioSnapshotPackageCached = true; 335 } 336 return studioSnapshotPackage; 337 } 338 339 /** 340 * @return null if there is no SNAPSHOT package 341 * @since 7.10 342 */ 343 public String getSnapshotStudioProjectName() { 344 DownloadablePackage snapshotPkg = getStudioProjectSnapshot(); 345 if (snapshotPkg != null) { 346 return snapshotPkg.getName(); 347 } 348 return null; 349 } 350 351 public String getStudioInstallationStatus() { 352 if (studioSnapshotStatus == null) { 353 LocalPackage pkg = null; 354 DownloadablePackage snapshotPkg = getStudioProjectSnapshot(); 355 if (snapshotPkg != null) { 356 try { 357 PackageUpdateService pus = Framework.getService(PackageUpdateService.class); 358 pkg = pus.getPackage(snapshotPkg.getId()); 359 } catch (PackageException e) { 360 log.error(e); 361 } 362 } 363 if (pkg == null) { 364 return translate(LABEL_STUDIO_UPDATE_STATUS + "noStatus"); 365 } 366 PackageState studioPkgState = pkg.getPackageState(); 367 if (studioPkgState == PackageState.DOWNLOADING) { 368 studioSnapshotStatus = SnapshotStatus.downloading; 369 } else if (studioPkgState == PackageState.DOWNLOADED) { 370 studioSnapshotStatus = SnapshotStatus.saving; 371 } else if (studioPkgState == PackageState.INSTALLING) { 372 studioSnapshotStatus = SnapshotStatus.installing; 373 } else if (studioPkgState.isInstalled()) { 374 studioSnapshotStatus = SnapshotStatus.completed; 375 } else { 376 studioSnapshotStatus = SnapshotStatus.error; 377 } 378 } 379 380 Object[] params = new Object[0]; 381 if (SnapshotStatus.error.equals(studioSnapshotStatus)) { 382 if (studioSnapshotUpdateError == null) { 383 studioSnapshotUpdateError = "???"; 384 } 385 params = new Object[] { studioSnapshotUpdateError }; 386 } else if (SnapshotStatus.downloading.equals(studioSnapshotStatus)) { 387 params = new Object[] { String.valueOf(studioSnapshotDownloadProgress) }; 388 } else { 389 FileTime update = getLastUpdateDate(); 390 if (update != null) { 391 DateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); 392 df.setTimeZone(TimeZone.getDefault()); 393 params = new Object[] { df.format(new Date(update.toMillis())) }; 394 } 395 } 396 397 return translate(LABEL_STUDIO_UPDATE_STATUS + studioSnapshotStatus.name(), params); 398 } 399 400 // TODO: plug a notifier for status to be shown to the user 401 protected class StudioAutoInstaller implements Runnable { 402 403 protected final String packageId; 404 405 protected final PackageManager pm; 406 407 /** 408 * @since 5.7.1 409 */ 410 protected final boolean validate; 411 412 protected StudioAutoInstaller(PackageManager pm, String packageId, boolean validate) { 413 this.pm = pm; 414 this.packageId = packageId; 415 this.validate = validate; 416 } 417 418 @Override 419 public void run() { 420 if (validate) { 421 ValidationStatus status = new ValidationStatus(); 422 423 pm.flushCache(); 424 DownloadablePackage remotePkg = pm.findRemotePackageById(packageId); 425 if (remotePkg == null) { 426 status.addError(String.format("Cannot perform validation: remote package '%s' not found", packageId)); 427 return; 428 } 429 430 PackageDependency[] pkgDeps = remotePkg.getDependencies(); 431 if (log.isDebugEnabled()) { 432 log.debug(String.format("%s target platforms: %s", remotePkg, 433 ArrayUtils.toString(remotePkg.getTargetPlatforms()))); 434 log.debug(String.format("%s dependencies: %s", remotePkg, ArrayUtils.toString(pkgDeps))); 435 } 436 437 // TODO NXP-11776: replace errors by internationalized labels 438 String targetPlatform = PlatformVersionHelper.getPlatformFilter(); 439 if (!TargetPlatformFilterHelper.isCompatibleWithTargetPlatform(remotePkg, targetPlatform)) { 440 status.addError(String.format("This package is not validated for your current platform: %s", 441 targetPlatform)); 442 } 443 // check deps requirements 444 if (pkgDeps != null && pkgDeps.length > 0) { 445 DependencyResolution resolution = pm.resolveDependencies(packageId, targetPlatform); 446 if (resolution.isFailed() && targetPlatform != null) { 447 // retry without PF filter in case it gives more information 448 resolution = pm.resolveDependencies(packageId, null); 449 } 450 if (resolution.isFailed()) { 451 status.addError(String.format("Dependency check has failed for package '%s' (%s)", packageId, 452 resolution)); 453 } else { 454 List<String> pkgToInstall = resolution.getInstallPackageIds(); 455 if (pkgToInstall != null && pkgToInstall.size() == 1 && packageId.equals(pkgToInstall.get(0))) { 456 // ignore 457 } else if (resolution.requireChanges()) { 458 // do not install needed deps: they may not be hot-reloadable and that's not what the 459 // "update snapshot" button is for. 460 status.addError(resolution.toString().trim().replaceAll("\n", "<br />")); 461 } 462 } 463 } 464 465 if (status.hasErrors()) { 466 setStatus(SnapshotStatus.error, translate("label.studio.update.validation.error"), status); 467 return; 468 } 469 } 470 471 // Effective install 472 if (Framework.isDevModeSet()) { 473 hotReloadPackage(); 474 } else { 475 InstallAfterRestart.addPackageForInstallation(packageId); 476 setStatus(SnapshotStatus.restartNeeded, null); 477 setupWizardAction.setNeedsRestart(true); 478 } 479 } 480 481 private void hotReloadPackage() { 482 Watch watch = new Watch().start(); 483 boolean useCompatReload = Framework.isBooleanPropertyTrue(ReloadService.USE_COMPAT_HOT_RELOAD); 484 485 PackageUpdateService pus = Framework.getService(PackageUpdateService.class); 486 try { 487 if (!useCompatReload) { 488 try { 489 setStatus(SnapshotStatus.installing, null); 490 log.info("Use hot reload update mechanism"); 491 ReloadHelper.hotReloadPackage(packageId); 492 // Refresh state 493 lastUpdate = pus.getInstallDate(packageId); 494 setStatus(SnapshotStatus.completed, null); 495 return; 496 } catch (NuxeoException e) { 497 log.error("Error while updating studio snapshot", e); 498 Throwable cause = e.getCause(); 499 if (cause instanceof ConnectServerError) { 500 setStatus(SnapshotStatus.error, e.getMessage()); 501 } else if (cause instanceof PackageException) { 502 setStatus(SnapshotStatus.error, translate("label.studio.update.installation.error", e.getMessage())); 503 } else if (cause instanceof InterruptedException) { 504 setStatus(SnapshotStatus.error, translate("label.studio.update.downloading.error", e.getMessage())); 505 Thread.currentThread().interrupt(); 506 throw e; 507 } 508 } 509 } 510 try { 511 LocalPackage pkg = pus.getPackage(packageId); 512 513 // Uninstall and/or remove if needed 514 if (pkg != null) { 515 watch.start("uninstall"); 516 log.info(String.format("Removing package %s before update...", pkg)); 517 if (pkg.getPackageState().isInstalled()) { 518 // First remove it to allow SNAPSHOT upgrade 519 log.info("Uninstalling " + packageId); 520 Task uninstallTask = pkg.getUninstallTask(); 521 try { 522 performTask(uninstallTask); 523 } catch (PackageException e) { 524 uninstallTask.rollback(); 525 throw e; 526 } 527 } 528 pus.removePackage(packageId); 529 watch.stop("uninstall"); 530 } 531 532 // Download 533 watch.start("download"); 534 setStatus(SnapshotStatus.downloading, null); 535 DownloadingPackage downloadingPkg = pm.download(packageId); 536 while (!downloadingPkg.isCompleted()) { 537 studioSnapshotDownloadProgress = downloadingPkg.getDownloadProgress(); 538 log.debug("downloading studio snapshot package"); 539 Thread.sleep(100); 540 } 541 studioSnapshotDownloadProgress = downloadingPkg.getDownloadProgress(); 542 setStatus(SnapshotStatus.saving, null); 543 watch.stop("download"); 544 545 // Install 546 watch.start("install"); 547 setStatus(SnapshotStatus.installing, null); 548 log.info("Installing " + packageId); 549 pkg = pus.getPackage(packageId); 550 if (pkg == null || PackageState.DOWNLOADED != pkg.getPackageState()) { 551 log.error("Error while downloading studio snapshot " + pkg); 552 setStatus(SnapshotStatus.error, translate("label.studio.update.downloading.error", pkg)); 553 return; 554 } 555 Task installTask = pkg.getInstallTask(); 556 try { 557 performTask(installTask); 558 } catch (PackageException e) { 559 installTask.rollback(); 560 throw e; 561 } 562 // Refresh state 563 pkg = pus.getPackage(packageId); 564 lastUpdate = pus.getInstallDate(packageId); 565 setStatus(SnapshotStatus.completed, null); 566 watch.stop("install"); 567 } catch (ConnectServerError e) { 568 setStatus(SnapshotStatus.error, e.getMessage()); 569 } catch (InterruptedException e) { 570 log.error("Error while downloading studio snapshot", e); 571 setStatus(SnapshotStatus.error, translate("label.studio.update.downloading.error", e.getMessage())); 572 ExceptionUtils.checkInterrupt(e); 573 } catch (PackageException e) { 574 log.error("Error while installing studio snapshot", e); 575 setStatus(SnapshotStatus.error, translate("label.studio.update.installation.error", e.getMessage())); 576 } 577 } finally { 578 watch.stop(); 579 if (log.isInfoEnabled()) { 580 StringBuilder message = new StringBuilder(); 581 message.append("Hot reload has been done in ") 582 .append(watch.getTotal().elapsed(TimeUnit.MILLISECONDS)) 583 .append(" ms, detailed steps:"); 584 Stream.of(watch.getIntervals()).filter(TimeInterval::isStopped).forEach( 585 i -> message.append("\n- ") 586 .append(i.getName()) 587 .append(": ") 588 .append(i.elapsed(TimeUnit.MILLISECONDS)) 589 .append(" ms")); 590 log.info(message.toString()); 591 } 592 } 593 } 594 595 protected void performTask(Task task) throws PackageException { 596 ValidationStatus validationStatus = task.validate(); 597 if (validationStatus.hasErrors()) { 598 throw new PackageException("Failed to validate package " + task.getPackage().getId() + " -> " 599 + validationStatus.getErrors()); 600 } 601 if (validationStatus.hasWarnings()) { 602 log.warn("Got warnings on package validation " + task.getPackage().getId() + " -> " 603 + validationStatus.getWarnings()); 604 } 605 task.run(null); 606 } 607 } 608 609 protected void setStatus(SnapshotStatus status, String errorMessage) { 610 studioSnapshotStatus = status; 611 studioSnapshotUpdateError = errorMessage; 612 } 613 614 protected void setStatus(SnapshotStatus status, String errorMessage, ValidationStatus validationStatus) { 615 setStatus(status, errorMessage); 616 setStudioSnapshotValidationStatus(validationStatus); 617 } 618 619 /** 620 * @since 5.7.1 621 */ 622 public ValidationStatus getStudioSnapshotValidationStatus() { 623 return studioSnapshotValidationStatus; 624 } 625 626 /** 627 * @since 5.7.1 628 */ 629 public void setStudioSnapshotValidationStatus(ValidationStatus status) { 630 studioSnapshotValidationStatus = status; 631 } 632 633 /** 634 * @since 5.7.1 635 */ 636 public void resetStudioSnapshotValidationStatus() { 637 setStudioSnapshotValidationStatus(null); 638 } 639 640 public void setDevMode(boolean value) { 641 String feedbackCompId = "changeDevModeForm"; 642 ConfigurationGenerator conf = setupWizardAction.getConfigurationGenerator(); 643 boolean configurable = conf.isConfigurable(); 644 if (!configurable) { 645 facesMessages.addToControl(feedbackCompId, StatusMessage.Severity.ERROR, 646 translate("label.setup.nuxeo.org.nuxeo.dev.changingDevModeNotConfigurable")); 647 return; 648 } 649 Map<String, String> params = new HashMap<>(); 650 params.put(Framework.NUXEO_DEV_SYSTEM_PROP, Boolean.toString(value)); 651 try { 652 conf.saveFilteredConfiguration(params); 653 conf.getServerConfigurator().dumpProperties(conf.getUserConfig()); 654 // force reload of framework properties to ensure it's immediately 655 // taken into account by all code checking for 656 // Framework#isDevModeSet 657 Framework.getRuntime().reloadProperties(); 658 659 if (value) { 660 facesMessages.addToControl(feedbackCompId, StatusMessage.Severity.WARN, 661 translate("label.admin.center.devMode.justActivated")); 662 } else { 663 facesMessages.addToControl(feedbackCompId, StatusMessage.Severity.INFO, 664 translate("label.admin.center.devMode.justDisabled")); 665 } 666 } catch (ConfigurationException | IOException e) { 667 log.error(e, e); 668 facesMessages.addToControl(feedbackCompId, StatusMessage.Severity.ERROR, 669 translate("label.admin.center.devMode.errorSaving", e.getMessage())); 670 } finally { 671 setupWizardAction.setNeedsRestart(true); 672 setupWizardAction.resetParameters(); 673 Contexts.getEventContext().remove("nxDevModeSet"); 674 } 675 } 676}