/******************************************************************************* * Copyright (c) 2015, 2016 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.boot.dash.model; import java.util.Arrays; import java.util.LinkedHashSet; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.springframework.ide.eclipse.boot.dash.BootDashActivator; import org.springframework.ide.eclipse.boot.dash.livexp.LiveSets; import org.springframework.ide.eclipse.boot.dash.metadata.PropertyStoreApi; import org.springframework.ide.eclipse.boot.dash.model.requestmappings.TypeLookup; import org.springframework.ide.eclipse.boot.dash.util.CancelationTokens; import org.springframework.ide.eclipse.boot.dash.util.CancelationTokens.CancelationToken; import org.springframework.ide.eclipse.boot.dash.util.Utils; import org.springframework.ide.eclipse.boot.dash.views.sections.BootDashColumn; import org.springframework.web.client.RestTemplate; import org.springsource.ide.eclipse.commons.livexp.core.DisposeListener; import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression; import org.springsource.ide.eclipse.commons.livexp.core.ObservableSet; import org.springsource.ide.eclipse.commons.livexp.core.ValueListener; import com.google.common.collect.ImmutableSet; /** * Abstract base class that is convenient to implement {@link BootDashElement}. * @author Kris De Volder */ public abstract class WrappingBootDashElement<T> extends AbstractDisposable implements BootDashElement { public static final String TAGS_KEY = "tags"; private static final String DEFAULT_RM_PATH_KEY = "default.request-mapping.path"; public static final String DEFAULT_RM_PATH_DEFAULT = "/"; protected final T delegate; private CancelationTokens cancelationTokens = new CancelationTokens(); private BootDashModel bootDashModel; private TypeLookup typeLookup; private ListenerList disposeListeners = new ListenerList(); @SuppressWarnings("rawtypes") private ValueListener elementStateNotifier = new ValueListener() { public void gotValue(LiveExpression exp, Object value) { getBootDashModel().notifyElementChanged(WrappingBootDashElement.this); } }; private ValueListener<?> elementNotifier; @Override public BootDashColumn[] getColumns() { return getTarget().getDefaultColumns(); } public WrappingBootDashElement(BootDashModel bootDashModel, T delegate) { this.bootDashModel = bootDashModel; this.delegate = delegate; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((delegate == null) ? 0 : delegate.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; @SuppressWarnings("rawtypes") WrappingBootDashElement other = (WrappingBootDashElement) obj; if (delegate == null) { if (other.delegate != null) return false; } else if (!delegate.equals(other.delegate)) return false; return true; } @Override public String toString() { return delegate.toString(); } protected TypeLookup getTypeLookup() { if (typeLookup==null) { typeLookup = new TypeLookup() { public IType findType(String fqName) { try { IJavaProject jp = getJavaProject(); if (jp!=null) { return jp.findType(fqName, new NullProgressMonitor()); } } catch (Exception e) { BootDashActivator.log(e); } return null; } }; } return typeLookup; } public abstract PropertyStoreApi getPersistentProperties(); @Override public LinkedHashSet<String> getTags() { try { String[] tags = getPersistentProperties() == null ? null : getPersistentProperties().get(TAGS_KEY, (String[])null); if (tags!=null) { return new LinkedHashSet<>(Arrays.asList(tags)); } } catch (Exception e) { BootDashActivator.log(e); } return new LinkedHashSet<>(); } @Override public void setTags(LinkedHashSet<String> newTags) { try { if (newTags==null || newTags.isEmpty()) { getPersistentProperties().put(TAGS_KEY, (String[])null); } else { getPersistentProperties().put(TAGS_KEY, newTags.toArray(new String[newTags.size()])); } bootDashModel.notifyElementChanged(this); } catch (Exception e) { BootDashActivator.log(e); } } @Override public final String getDefaultRequestMappingPath() { String storedValue = getPersistentProperties() == null ? null : getPersistentProperties().get(DEFAULT_RM_PATH_KEY); if (storedValue!=null) { return storedValue; } //inherit a default value from parent node? Object parent = getParent(); if (parent instanceof BootDashElement) { String inheritedValue = ((BootDashElement) parent).getDefaultRequestMappingPath(); return inheritedValue; } return null; } @Override public final void setDefaultRequestMappingPath(String defaultPath) { try { getPersistentProperties().put(DEFAULT_RM_PATH_KEY, defaultPath); bootDashModel.notifyElementChanged(this); } catch (Exception e) { BootDashActivator.log(e); } } @Override public boolean hasDevtools() { return false; } public BootDashModel getBootDashModel() { return bootDashModel; } @Override public IJavaProject getJavaProject() { return getProject() != null ? JavaCore.create(getProject()) : null; } @Override public ObservableSet<BootDashElement> getChildren() { return LiveSets.emptySet(BootDashElement.class); } @Override public ImmutableSet<BootDashElement> getCurrentChildren() { return getChildren().getValue(); } @Override public void onDispose(DisposeListener listener) { this.disposeListeners.add(listener); } @Override public void dispose() { for (Object l : disposeListeners.getListeners()) { ((DisposeListener)l).disposed(this); } } @Override public ImmutableSet<ILaunchConfiguration> getLaunchConfigs() { //Default implementation for BDEs that do not have any relation to launch configs //Subclass should override when elements relate to launch configs. return ImmutableSet.of(); } /** * Gets a summary of the 'livePorts' for this node and its children. This default implementation * is provided for nodes that only have a single port. Nodes that need to compute an actual * summary should override this. */ @Override public ImmutableSet<Integer> getLivePorts() { int port = getLivePort(); if (port>0) { return ImmutableSet.of(port); } else { return ImmutableSet.of(); } } protected RestTemplate getRestTemplate() { return new RestTemplate(); } /** * Ensure that element state notifications are fired when a given liveExp's value changes. */ @SuppressWarnings("unchecked") protected void addElementState(LiveExpression<?> state) { state.addListener(elementStateNotifier); } @Override public String getUrl() { return Utils.createUrl(getLiveHost(), getLivePort(), getDefaultRequestMappingPath()); } private synchronized ValueListener<?> getElementNotifier() { if (elementNotifier==null) { elementNotifier = new ValueListener<Object>() { @Override public void gotValue(LiveExpression<Object> exp, Object value) { getBootDashModel().notifyElementChanged(WrappingBootDashElement.this); } }; } return elementNotifier; } /** * Attach a listener to a given liveExp so that the model's 'notifyElementChanged' is called * any time the liveExps value changes. */ @SuppressWarnings("unchecked") protected void addElementNotifier(LiveExpression<?> exp) { @SuppressWarnings("rawtypes") ValueListener notifier = getElementNotifier(); exp.addListener(notifier); } public CancelationToken createCancelationToken() { return cancelationTokens.create(); } public void cancelOperations() { cancelationTokens.cancelAll(); } }