| /*=============================================================================# |
| # Copyright (c) 2010, 2019 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.internal.rhelp.core; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| |
| import org.eclipse.statet.jcommons.collections.ImCollections; |
| import org.eclipse.statet.jcommons.collections.ImList; |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| import org.eclipse.statet.jcommons.status.ErrorStatus; |
| import org.eclipse.statet.jcommons.status.InfoStatus; |
| import org.eclipse.statet.jcommons.status.Status; |
| import org.eclipse.statet.jcommons.status.StatusException; |
| import org.eclipse.statet.jcommons.status.Statuses; |
| |
| import org.eclipse.statet.internal.rhelp.core.server.ServerClientSupport; |
| import org.eclipse.statet.internal.rhelp.core.server.ServerREnvHelpAccess; |
| import org.eclipse.statet.rhelp.core.REnvHelpConfiguration; |
| import org.eclipse.statet.rhelp.core.RHelpCore; |
| import org.eclipse.statet.rj.renv.core.REnv; |
| import org.eclipse.statet.rj.renv.core.REnvConfiguration; |
| import org.eclipse.statet.rj.renv.core.REnvManager; |
| |
| |
| @NonNullByDefault |
| public class RHelpManagerIntern { |
| |
| |
| private static @Nullable Object getStateLocation(final REnvHelpConfiguration config) { |
| switch (config.getStateSharedType()) { |
| case REnvConfiguration.SHARED_DIRECTORY: |
| return config.getStateSharedDirectoryPath(); |
| case REnvConfiguration.SHARED_SERVER: |
| return config.getStateSharedServerUri(); |
| default: |
| return null; |
| } |
| } |
| |
| private static boolean isConfigEqual(final REnvHelpConfiguration config1, |
| final @Nullable REnvHelpConfiguration config2) { |
| return (config2 != null |
| && config1.getStateSharedType() == config2.getStateSharedType() |
| && Objects.equals(getStateLocation(config1), getStateLocation(config2)) ); |
| } |
| |
| |
| private static final int HELP_LOADED= 2; |
| private static final int HELP_AVAILABLE= 1; |
| private static final int UNKNOWN= 0; |
| private static final int HELP_MISSING= -1; |
| private static final int RENV_DELETED= -2; |
| |
| |
| private static class EnvItem { |
| |
| final REnv rEnv; |
| |
| int state; |
| |
| long localStamp; |
| |
| @Nullable REnvHelpImpl help; |
| private String configStateType; |
| private @Nullable Object configStateLocation; |
| |
| final Object helpLock= new Object(); |
| |
| boolean indexUpdate; |
| boolean indexCheck; |
| |
| @Nullable Throwable lastServerException; |
| |
| |
| public EnvItem(final REnv rEnv) { |
| this.rEnv= rEnv; |
| updateConfig(null); |
| unset((rEnv.isDeleted()) ? RENV_DELETED : UNKNOWN); |
| } |
| |
| |
| public boolean isConfigured() { |
| return (this.state == HELP_LOADED || this.state == HELP_MISSING); |
| } |
| |
| public void updateConfig(final @Nullable REnvHelpConfiguration config) { |
| final String newStateType; |
| final Object newStateLocation; |
| if (config != null) { |
| newStateType= config.getStateSharedType(); |
| newStateLocation= getStateLocation(config); |
| } |
| else { |
| newStateType= ""; //$NON-NLS-1$ |
| newStateLocation= null; |
| } |
| if (newStateType != this.configStateType |
| || !Objects.equals(newStateLocation, this.configStateLocation) ) { |
| this.configStateType= newStateType; |
| this.configStateLocation= newStateLocation; |
| this.lastServerException= null; |
| } |
| } |
| |
| public void updateLocalStamp(final long stamp) { |
| assert (this.help == null); |
| this.state= (stamp != REnvHelpImpl.NOT_AVAILABLE_STAMP) ? |
| HELP_AVAILABLE : HELP_MISSING; |
| this.localStamp= stamp; |
| } |
| |
| public @Nullable REnvHelpImpl set(final @Nullable REnvHelpImpl help) { |
| if (help == null) { |
| return unset(HELP_MISSING); |
| } |
| final REnvHelpImpl oldHelp= this.help; |
| this.state= HELP_LOADED; |
| this.localStamp= help.getStamp(); |
| this.help= help; |
| return oldHelp; |
| } |
| |
| public @Nullable REnvHelpImpl unset(final int state) { |
| final REnvHelpImpl oldHelp= this.help; |
| this.state= state; |
| this.localStamp= REnvHelpImpl.NOT_AVAILABLE_STAMP; |
| this.help= null; |
| return oldHelp; |
| } |
| |
| public boolean isConfigEqual(final @Nullable REnvHelpConfiguration config) { |
| return (config != null |
| && this.configStateType == config.getStateSharedType() |
| && Objects.equals(this.configStateLocation, getStateLocation(config))); |
| } |
| |
| } |
| |
| private static boolean isEquals(final @Nullable Throwable e1, final @Nullable Throwable e2) { |
| if (e1 == e2) { |
| return true; |
| } |
| if (e1 != null && e2 != null) { |
| return (e1.getClass() == e2.getClass() |
| && Objects.equals(e1.getMessage(), e2.getMessage()) |
| && isEquals(e1.getCause(), e2.getCause()) ); |
| } |
| return false; |
| } |
| |
| |
| protected final REnvManager rEnvManager; |
| |
| private final Object indexLock= new Object(); |
| |
| private final Map<String, EnvItem> helpIndexes= new HashMap<>(); |
| |
| private final SerUtil serUtil= new SerUtil(); |
| |
| |
| public RHelpManagerIntern(final REnvManager rEnvManager) { |
| this.rEnvManager= rEnvManager; |
| } |
| |
| |
| public @Nullable REnv getREnv(final @Nullable String id) { |
| return this.rEnvManager.get(id, null); |
| } |
| |
| |
| private EnvItem getItem(final REnv rEnv) { |
| synchronized (this.indexLock) { |
| EnvItem item= this.helpIndexes.get(rEnv.getId()); |
| if (item == null) { |
| item= new EnvItem(rEnv); |
| this.helpIndexes.put(rEnv.getId(), item); |
| } |
| return item; |
| } |
| } |
| |
| private ImList<EnvItem> getItems() { |
| return ImCollections.toList(this.helpIndexes.values()); |
| } |
| |
| |
| public @Nullable Object beginIndexUpdate(final REnv rEnv) { |
| final EnvItem item= getItem(rEnv); |
| synchronized (item.helpLock) { |
| if (item.indexUpdate) { |
| return null; |
| } |
| else { |
| item.indexUpdate= true; |
| return item; |
| } |
| } |
| } |
| |
| public void endIndexUpdate(final Object lock) { |
| final EnvItem item= (EnvItem) lock; |
| synchronized (item.helpLock) { |
| item.indexUpdate= false; |
| } |
| } |
| |
| public @Nullable Object beginIndexCheck(final REnv rEnv) { |
| final EnvItem item= getItem(rEnv); |
| synchronized (item.helpLock) { |
| if (item.indexCheck || item.indexUpdate) { |
| return null; |
| } |
| else { |
| item.indexCheck= true; |
| return item; |
| } |
| } |
| } |
| |
| public void endIndexCheck(final Object lock) { |
| final EnvItem item= (EnvItem) lock; |
| synchronized (item.helpLock) { |
| item.indexCheck= false; |
| } |
| } |
| |
| |
| public void checkREnvs() { |
| final List<EnvItem> items; |
| final ImList<? extends REnv> rEnvs= this.rEnvManager.list(); |
| synchronized (this.indexLock) { |
| for (final REnv rEnv : rEnvs) { |
| getItem(rEnv); |
| } |
| items= new ArrayList<>(this.helpIndexes.values()); |
| } |
| |
| for (final EnvItem item : items) { |
| REnvHelpImpl oldHelp= null; |
| try { |
| synchronized (item.helpLock) { |
| if (item.rEnv.isDeleted()) { |
| oldHelp= item.unset(RENV_DELETED); |
| } |
| else { |
| final REnvHelpConfiguration rEnvConfig= item.rEnv.get(REnvHelpConfiguration.class); |
| if (item.isConfigured()) { |
| if (item.isConfigEqual(rEnvConfig)) { |
| continue; |
| } |
| else { |
| oldHelp= item.unset(UNKNOWN); |
| } |
| } |
| item.updateConfig(rEnvConfig); |
| } |
| } |
| } |
| finally { |
| if (oldHelp != null) { |
| oldHelp.dispose(); |
| } |
| } |
| } |
| } |
| |
| private static class SerSaveController implements SerUtil.Controller { |
| |
| |
| private final EnvItem item; |
| private final REnvHelpConfiguration rEnvConfig; |
| |
| protected @Nullable REnvHelpImpl oldHelp; |
| |
| |
| public SerSaveController(final EnvItem item, final REnvHelpConfiguration rEnvConfig) { |
| this.item= item; |
| this.rEnvConfig= rEnvConfig; |
| } |
| |
| |
| public EnvItem getItem() { |
| return this.item; |
| } |
| |
| @Override |
| public Object getFileLock() { |
| return this.item.helpLock; |
| } |
| |
| @Override |
| public boolean shouldSave() { |
| return (this.item.state != RENV_DELETED |
| && isConfigEqual(this.rEnvConfig, this.item.rEnv.get(REnvHelpConfiguration.class)) ); |
| } |
| |
| @Override |
| public void onSaved() { |
| } |
| |
| public void dispose() { |
| if (this.oldHelp != null) { |
| this.oldHelp.dispose(); |
| } |
| } |
| |
| } |
| |
| public boolean updateLocalHelp(final REnvHelpConfiguration rEnvConfig, |
| final @Nullable Map<String, String> rEnvSharedProperties, final REnvHelpImpl newHelp) { |
| final REnv rEnv= newHelp.getREnv(); |
| |
| final SerSaveController controller= new SerSaveController(getItem(rEnv), rEnvConfig) { |
| @Override |
| public void onSaved() { |
| final EnvItem item= getItem(); |
| item.updateConfig(rEnvConfig); |
| this.oldHelp= item.set(newHelp); |
| // TODO REMOTE |
| // if (rEnvConfig instanceof REnvConfiguration) { |
| // ((REnvConfiguration) rEnvConfig).updateSharedProperties(rEnvSharedProperties); |
| // } |
| } |
| }; |
| try { |
| this.serUtil.save(rEnvConfig, newHelp, controller); |
| |
| return true; |
| } |
| finally { |
| controller.dispose(); |
| } |
| } |
| |
| public void updateServerHelp() { |
| try { |
| final ServerClientSupport serverSupport= ServerClientSupport.getInstance(); |
| |
| final ImList<EnvItem> items= getItems(); |
| |
| for (final EnvItem item : items) { |
| if (item.rEnv.isDeleted()) { |
| continue; |
| } |
| updateServerHelp(item, serverSupport); |
| } |
| } |
| catch (final Exception e) { |
| RHelpCoreInternals.log(new ErrorStatus(RHelpCore.BUNDLE_ID, |
| "An error occured while updating R help data for R environments.", |
| e )); |
| } |
| } |
| |
| private void updateServerHelp(final EnvItem item, final ServerClientSupport serverSupport) { |
| final REnvHelpConfiguration rEnvConfig; |
| long currentStamp; |
| synchronized (item.helpLock) { |
| if (item.configStateType != REnvConfiguration.SHARED_SERVER) { |
| return; |
| } |
| rEnvConfig= item.rEnv.get(REnvHelpConfiguration.class); |
| if (rEnvConfig == null) { |
| return; |
| } |
| if (item.state == UNKNOWN) { |
| item.updateLocalStamp(this.serUtil.getStamp(rEnvConfig)); |
| } |
| currentStamp= item.localStamp; |
| } |
| |
| try { |
| final ServerREnvHelpAccess serverAccess= serverSupport.getREnvHelpAccess(rEnvConfig); |
| if (serverAccess.loadREnvHelpData(rEnvConfig, currentStamp, |
| new SerSaveController(item, rEnvConfig) )) { |
| REnvHelpImpl newHelp= null; |
| REnvHelpImpl oldHelp= null; |
| try { |
| synchronized (item.helpLock) { |
| switch (item.state) { |
| case RENV_DELETED: |
| break; |
| case HELP_LOADED: |
| item.updateConfig(rEnvConfig); |
| newHelp= this.serUtil.load(rEnvConfig); |
| oldHelp= item.set(newHelp); |
| break; |
| default: |
| item.updateLocalStamp(this.serUtil.getStamp(rEnvConfig)); |
| break; |
| } |
| } |
| if (newHelp != null) { |
| newHelp.setIndex(serverAccess); |
| } |
| } |
| finally { |
| if (oldHelp != null) { |
| oldHelp.dispose(); |
| } |
| } |
| |
| RHelpCoreInternals.log(new InfoStatus(RHelpCore.BUNDLE_ID, |
| String.format("Successfully updated R help data for R environment '%1$s'.", |
| item.rEnv.getName() ))); |
| } |
| } |
| catch (final Exception e) { |
| if (isEquals(item.lastServerException, e)) { |
| return; |
| } |
| item.lastServerException= e; |
| |
| byte severity= Status.ERROR; |
| String message= null; |
| if (e instanceof StatusException) { |
| final Status status= ((StatusException) e).getStatus(); |
| switch (status.getCode()) { |
| // case REnvHelpIndex.TIMEOUT_ERROR: |
| case REnvHelpIndex.CONNECT_ERROR: |
| severity= Status.INFO; |
| message= String.format("Updating R help data for R environment '%1$s' failed. " + |
| "It seems the R help server is currently not available.", |
| item.rEnv.getName() ); |
| break; |
| default: |
| severity= status.getSeverity(); |
| break; |
| } |
| } |
| RHelpCoreInternals.log(Statuses.newStatus(severity, RHelpCore.BUNDLE_ID, |
| (message != null) ? message : |
| String.format("An error occured when updating R help data for R environment '%1$s'.", |
| item.rEnv.getName() ), |
| e )); |
| } |
| } |
| |
| |
| public List<REnv> getREnvWithHelpIntern() { |
| final ImList<EnvItem> items= getItems(); |
| |
| final List<REnv> withHelp= new ArrayList<>(items.size()); |
| for (final EnvItem item : items) { |
| if (item.rEnv.isDeleted()) { |
| continue; |
| } |
| if (hasHelp(item)) { |
| withHelp.add(item.rEnv); |
| } |
| } |
| return withHelp; |
| } |
| |
| public boolean hasHelpIntern(final REnv rEnv) { |
| if (rEnv.isDeleted()) { |
| return false; |
| } |
| final EnvItem item= getItem(rEnv); |
| return hasHelp(item); |
| } |
| |
| private boolean hasHelp(final EnvItem item) { |
| synchronized (item.helpLock) { |
| if (item.state == UNKNOWN) { |
| final REnvHelpConfiguration rEnvConfig= item.rEnv.get(REnvHelpConfiguration.class); |
| item.updateLocalStamp((rEnvConfig != null) ? |
| this.serUtil.getStamp(rEnvConfig) : REnvHelpImpl.NOT_AVAILABLE_STAMP); |
| } |
| switch (item.state) { |
| case HELP_LOADED: |
| case HELP_AVAILABLE: |
| return true; |
| default: |
| return false; |
| } |
| } |
| } |
| |
| public @Nullable REnvHelpImpl getHelpIntern(final REnv rEnv) { |
| final EnvItem item= getItem(rEnv); |
| synchronized (item.helpLock) { |
| switch (item.state) { |
| case HELP_LOADED: |
| item.help.lock(); |
| return item.help; |
| case HELP_AVAILABLE: |
| case UNKNOWN: |
| final REnvHelpConfiguration rEnvConfig= rEnv.get(REnvHelpConfiguration.class); |
| if (rEnvConfig != null) { |
| item.updateConfig(rEnvConfig); |
| item.set(this.serUtil.load(rEnvConfig)); |
| } |
| if (item.help != null) { |
| item.help.lock(); |
| return item.help; |
| } |
| else { |
| return null; |
| } |
| default: |
| return null; |
| } |
| } |
| } |
| |
| } |