Package org.cloudfoundry.ide.eclipse.server.core.internal

Source Code of org.cloudfoundry.ide.eclipse.server.core.internal.ModuleCache

/*******************************************************************************
* Copyright (c) 2012, 2014 Pivotal Software, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Apache License,
* Version 2.0 (the "License�); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*  Contributors:
*     Pivotal Software, Inc. - initial API and implementation
********************************************************************************/
package org.cloudfoundry.ide.eclipse.server.core.internal;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.cloudfoundry.client.lib.domain.CloudApplication;
import org.cloudfoundry.ide.eclipse.server.core.internal.client.CloudFoundryApplicationModule;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.wst.server.core.IModule;
import org.eclipse.wst.server.core.IServer;
import org.eclipse.wst.server.core.IServerLifecycleListener;
import org.eclipse.wst.server.core.ServerCore;
import org.osgi.service.prefs.BackingStoreException;

/**
* Manages the cloud state of the modules in the form of {@link ServerData}.
* This can not be managed in the server or behavior delegate since those get
* disposed every time a working copy is saved. The module cache may be accessed
* by multiple threads therefore multi-threaded access needs to be taken into
* account when modifying server data state.
* @author Steffen Pingel
*/
public class ModuleCache {

  public static class ServerData {

    private final List<CloudFoundryApplicationModule> cloudModules = new ArrayList<CloudFoundryApplicationModule>();

    /** Cached password in case secure store fails. */
    private String password;

    private IServer server;

    /**
     * Modules added in this session.
     */
    private final List<IModule> undeployedModules = new ArrayList<IModule>();

    private final Map<String, RepublishModule> automaticRepublishModules = new HashMap<String, RepublishModule>();

    private int[] applicationMemoryChoices;

    ServerData(IServer server) {
      this.server = server;
    }

    public synchronized void clear() {
      cloudModules.clear();
    }

    public synchronized CloudFoundryApplicationModule createModule(CloudApplication application) {
      CloudFoundryApplicationModule appModule = new CloudFoundryApplicationModule(application.getName(), server);
      appModule.setCloudApplication(application);
      add(appModule);
      return appModule;
    }

    /**
     * Updates the cache of local module -> cloud module mapping. This is
     * used when the deployed name changes (e.g. a user specifies a
     * different deployment name than the local module name that typically
     * matches the workspace project name for the app, if the project is
     * accessible).
     * @param module whose mapping to a local module needs to be updated and
     * persisted.
     */
    public synchronized void updateCloudApplicationModule(CloudFoundryApplicationModule module) {
      // Update the map of module ID -> Deployed Application name
      if (module.getLocalModule() != null) {
        Map<String, String> mapping = getLocalModuleToCloudModuleMapping();
        mapping.put(module.getLocalModule().getId(), module.getDeployedApplicationName());
        setLocalModuleToCloudModuleMapping(mapping);
      }
    }

    /**
     *
     * @return never null. May be empty
     */
    public synchronized Collection<CloudFoundryApplicationModule> getExistingCloudModules() {
      return new ArrayList<CloudFoundryApplicationModule>(cloudModules);
    }

    public synchronized String getPassword() {
      return password;
    }

    public synchronized boolean isUndeployed(IModule module) {
      return undeployedModules.contains(module);
    }

    public synchronized void remove(CloudFoundryApplicationModule module) {
      cloudModules.remove(module);
      if (module.getLocalModule() != null) {
        Map<String, String> mapping = getLocalModuleToCloudModuleMapping();
        mapping.remove(module.getLocalModule().getId());
        setLocalModuleToCloudModuleMapping(mapping);
      }
    }

    public synchronized void removeObsoleteModules(Set<CloudFoundryApplicationModule> allModules) {
      HashSet<CloudFoundryApplicationModule> deletedModules = new HashSet<CloudFoundryApplicationModule>(
          cloudModules);
      deletedModules.removeAll(allModules);
      if (deletedModules.size() > 0) {
        Map<String, String> mapping = getLocalModuleToCloudModuleMapping();
        boolean mappingModified = false;
        for (CloudFoundryApplicationModule deletedModule : deletedModules) {
          if (deletedModule.getLocalModule() != null) {
            mappingModified |= mapping.remove(deletedModule.getLocalModule().getId()) != null;
          }
        }
        if (mappingModified) {
          setLocalModuleToCloudModuleMapping(mapping);
        }
      }
    }

    public synchronized void setPassword(String password) {
      this.password = password;
    }

    public synchronized void tagAsDeployed(IModule module) {
      undeployedModules.remove(module);
    }

    public synchronized void tagAsUndeployed(IModule module) {
      undeployedModules.add(module);
    }

    public synchronized void tagForAutomaticRepublish(RepublishModule module) {
      // Tag modules based on their local name, not the deployed name
      automaticRepublishModules.put(module.getModule().getName(), module);
    }

    public synchronized RepublishModule untagForAutomaticRepublish(IModule module) {
      return automaticRepublishModules.remove(module.getName());
    }

    private void add(CloudFoundryApplicationModule module) {
      cloudModules.add(module);
    }

    private String convertMapToString(Map<String, String> map) {
      if (map == null) {
        return ""; //$NON-NLS-1$
      }
      StringBuilder result = new StringBuilder();
      for (Map.Entry<String, String> entry : map.entrySet()) {
        result.append(entry.getKey());
        result.append(","); //$NON-NLS-1$
        result.append(entry.getValue());
        result.append(","); //$NON-NLS-1$
      }
      return result.toString();
    }

    private Map<String, String> convertStringToMap(String str) {
      if (str == null) {
        return new HashMap<String, String>();
      }
      Map<String, String> result = new HashMap<String, String>();
      String[] tokens = str.split(","); //$NON-NLS-1$
      for (int i = 0; i < tokens.length - 1; i += 2) {
        result.put(tokens[i], tokens[i + 1]);
      }
      return result;
    }

    /**
     * Local modules are mapped to deployed applications, represented by
     * cloud modules, by mapping the local module ID (typically, the module
     * type + local module name) to the deployed application name.
     * @return map containing local module ID (key) to deployed cloud
     * application name (value)
     */
    private Map<String, String> getLocalModuleToCloudModuleMapping() {
      IEclipsePreferences node = new InstanceScope().getNode(CloudFoundryPlugin.PLUGIN_ID);
      String string = node.get(KEY_MODULE_MAPPING_LIST + ":" + getServerId(), ""); //$NON-NLS-1$ //$NON-NLS-2$
      return convertStringToMap(string);
    }

    private CloudFoundryApplicationModule getCloudModuleByDeployedAppName(String deployedApplicationName) {
      for (CloudFoundryApplicationModule module : cloudModules) {
        if (deployedApplicationName.equals(module.getDeployedApplicationName())) {
          return module;
        }
      }
      return null;
    }

    /**
     * A {@link CloudFoundryApplicationModule} is a Cloud Foundry-aware
     * module representing a deployed application. If it exists, it means
     * that the application is currently or has been already processed by
     * the CF plugin. If it does not exist (its null), it means it still
     * needs to be created separately.
     * @param localName must be the local module name. In some cases it may
     * be the same as the deployed application name (in case the module is
     * external and has not corresponding accessible workspace project), but
     * they may be different as well, in case the local name (i.e. the
     * project name) differs from the user-defined deployed name.
     * @return
     */
    private CloudFoundryApplicationModule getCloudModuleToLocalModuleName(String localName) {
      for (CloudFoundryApplicationModule module : cloudModules) {
        if (localName.equals(module.getName())) {
          return module;
        }
      }
      return null;
    }

    private String getServerId() {
      return server.getAttribute(CloudFoundryServer.PROP_SERVER_ID, (String) null);
    }

    private void setLocalModuleToCloudModuleMapping(Map<String, String> list) {
      String string = convertMapToString(list);
      IEclipsePreferences node = new InstanceScope().getNode(CloudFoundryPlugin.PLUGIN_ID);
      CloudFoundryPlugin.trace("Updated mapping: " + string); //$NON-NLS-1$
      node.put(KEY_MODULE_MAPPING_LIST + ":" + getServerId(), string); //$NON-NLS-1$
      try {
        node.flush();
      }
      catch (BackingStoreException e) {
        CloudFoundryPlugin
            .getDefault()
            .getLog()
            .log(new Status(IStatus.ERROR, CloudFoundryPlugin.PLUGIN_ID,
                "Failed to update application mappings", e)); //$NON-NLS-1$
      }
    }

    synchronized CloudFoundryApplicationModule getExistingCloudModule(IModule module) {
      if (module == null) {
        return null;
      }
      // See if the cloud module for the given local IModule has been
      // created.
      CloudFoundryApplicationModule appModule = getCloudModuleToLocalModuleName(module.getName());
      if (appModule != null) {
        return appModule;
      }

      // Otherwise check if there is a mapping between the IModule ID and
      // the deployed application name, and
      // search for a cloud module that matches the deployed application
      // name
      String deployedAppName = getLocalModuleToCloudModuleMapping().get(module.getId());
      if (deployedAppName != null) {
        appModule = getCloudModuleByDeployedAppName(deployedAppName);
        if (appModule != null) {
          return appModule;
        }
        // If not available, it means it needs to be created below.
      }
      return null;
    }

    synchronized CloudFoundryApplicationModule getOrCreateCloudModule(IModule module) {

      // See if the cloud module for the given local IModule has been
      // created.
      CloudFoundryApplicationModule appModule = getExistingCloudModule(module);
      if (appModule != null) {
        return appModule;
      }

      // Otherwise check if there is a mapping between the IModule ID and
      // the deployed application name, and
      // search for a cloud module that matches the deployed application
      // name
      String deployedAppName = getLocalModuleToCloudModuleMapping().get(module.getId());
      if (deployedAppName == null) {
        deployedAppName = module.getName();
      }

      // no mapping found, create new Cloud Foundry-aware module. Note
      // that the
      // deployedAppName and the module name need not be the same.
      appModule = new CloudFoundryApplicationModule(module, deployedAppName, server);

      add(appModule);
      return appModule;
    }

    void updateServerId(String oldServerId, String newServerId) {
      IEclipsePreferences node = new InstanceScope().getNode(CloudFoundryPlugin.PLUGIN_ID);
      String string = node.get(KEY_MODULE_MAPPING_LIST + ":" + oldServerId, ""); //$NON-NLS-1$ //$NON-NLS-2$
      node.remove(KEY_MODULE_MAPPING_LIST + ":" + oldServerId); //$NON-NLS-1$
      node.put(KEY_MODULE_MAPPING_LIST + ":" + newServerId, string); //$NON-NLS-1$
    }

    public synchronized void setApplicationMemoryChoices(int[] applicationMemoryChoices) {
      this.applicationMemoryChoices = applicationMemoryChoices;
    }

    public synchronized int[] getApplicationMemoryChoices() {
      return applicationMemoryChoices;
    }
  }

  /**
   * List of appName, module id pairs.
   */
  static final String KEY_MODULE_MAPPING_LIST = "org.cloudfoundry.ide.eclipse.moduleMapping"; //$NON-NLS-1$

  private Map<IServer, ServerData> dataByServer;

  private IServerLifecycleListener listener = new IServerLifecycleListener() {

    public void serverAdded(IServer server) {
      // ignore
    }

    public void serverChanged(IServer server) {
      // ignore

    }

    public void serverRemoved(IServer server) {
      remove(server);
    }
  };

  public ModuleCache() {
    dataByServer = new HashMap<IServer, ServerData>();
    ServerCore.addServerLifecycleListener(listener);
  }

  public void dispose() {
    ServerCore.removeServerLifecycleListener(listener);
  }

  public synchronized ServerData getData(IServer server) {
    ServerData data = dataByServer.get(server);
    if (data == null && server != null) {
      data = new ServerData(server);
      dataByServer.put(server, data);
    }
    return data;
  }

  protected synchronized void remove(IServer server) {
    dataByServer.remove(server);

    String serverId = server.getAttribute(CloudFoundryServer.PROP_SERVER_ID, (String) null);
    if (serverId != null) {
      IEclipsePreferences node = new InstanceScope().getNode(CloudFoundryPlugin.PLUGIN_ID);
      node.remove(KEY_MODULE_MAPPING_LIST + ":" + serverId); //$NON-NLS-1$
      try {
        node.flush();
      }
      catch (BackingStoreException e) {
        CloudFoundryPlugin
            .getDefault()
            .getLog()
            .log(new Status(IStatus.ERROR, CloudFoundryPlugin.PLUGIN_ID,
                "Failed to remove application mappings", e)); //$NON-NLS-1$
      }
    }
  }

}
TOP

Related Classes of org.cloudfoundry.ide.eclipse.server.core.internal.ModuleCache

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.