/******************************************************************************* * Copyright (c) 2014, 2015 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 * IBM - Fix duplicate space check ********************************************************************************/ package org.cloudfoundry.ide.eclipse.server.ui.internal; import java.util.List; import org.cloudfoundry.client.lib.domain.CloudSpace; import org.cloudfoundry.ide.eclipse.server.core.internal.CloudFoundryBrandingExtensionPoint; import org.cloudfoundry.ide.eclipse.server.core.internal.CloudFoundryPlugin; import org.cloudfoundry.ide.eclipse.server.core.internal.CloudFoundryServer; import org.cloudfoundry.ide.eclipse.server.core.internal.CloudServerUtil; import org.cloudfoundry.ide.eclipse.server.core.internal.spaces.CloudFoundrySpace; import org.cloudfoundry.ide.eclipse.server.core.internal.spaces.CloudOrgsAndSpaces; import org.cloudfoundry.ide.eclipse.server.core.internal.spaces.CloudSpacesDescriptor; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.operation.IRunnableContext; import org.eclipse.osgi.util.NLS; /** * Resolves a list of orgs and spaces given a set of credentials and cloud * server URL. This is returned in the form of a {@link CloudSpacesDescriptor} * <p/> * Descriptors are cached per credentials and cloud server URL to prevent * frequent org/spaces request to the server. * <p/> * To obtain an updated list of descriptors, always create a new * CloudSpaceChangeHandler instance. * <p/> * Also Performs checks and validations on cloud spaces, and has API to set a * selected space that may be invoked by a space selection component, like a UI * part that displays available cloud spaces. * * * * */ public abstract class CloudSpacesDelegate { private final CloudFoundryServer cloudServer; private CloudSpacesDescriptor spacesDescriptor; protected final String serverServiceName; protected CloudSpacesDelegate(CloudFoundryServer cloudServer) { this.cloudServer = cloudServer; String serverTypeId = cloudServer.getServer().getServerType().getId(); serverServiceName = CloudFoundryBrandingExtensionPoint.getServiceName(serverTypeId); } public String getServerServiceName() { return serverServiceName; } /** * @return the first space available that has no corresponding server * instance. If null, no space found that is not already associated with a * server instance. */ public CloudSpace getSpaceWithNoServerInstance() { CloudSpacesDescriptor descriptor = getCurrentSpacesDescriptor(); CloudOrgsAndSpaces orgsSpaces = descriptor != null ? descriptor.getOrgsAndSpaces() : null; if (orgsSpaces != null) { List<CloudFoundryServer> cloudServers = CloudServerUtil.getCloudServers(); if (cloudServers == null || cloudServers.isEmpty()) { return orgsSpaces.getDefaultCloudSpace(); } else { List<CloudSpace> spaces = orgsSpaces.getAllSpaces(); if (spaces != null) { String url = cloudServer.getUrl(); for (CloudSpace space : spaces) { CloudFoundryServer foundServer = null; for (CloudFoundryServer existingServer : cloudServers) { // Be sure to only check existing servers with the // same URL, as other existing server instances may // have same // org and space name, but have different URL if (existingServer.getUrl().equals(url) && matchesSpace(space, existingServer.getCloudFoundrySpace())) { foundServer = cloudServer; break; } } if (foundServer == null) { return space; } } } } } return null; } /** * True if there is already a descriptor set for the given set of * credentials that contains a list of orgs and spaces. False otherwise * @param urlText * @param userName * @param password * @return */ public boolean matchesCurrentDescriptor(String urlText, String userName, String password, boolean selfSigned) { String actualURL = CloudUiUtil.getUrlFromDisplayText(urlText); String cachedDescriptorID = CloudSpacesDescriptor.getDescriptorID(userName, password, actualURL, selfSigned); // If there are no changes in credentials and URL, and a descriptor is // already present, do not do a lookup or notify listeners of changes. return getCurrentSpacesDescriptor() != null && getCurrentSpacesDescriptor().getID() != null && getCurrentSpacesDescriptor().getID().equals(cachedDescriptorID); } /** * Given space selection, determine if it is valid. For example, a user * wishes to create a server instance to the selected cloudSpace, if the * cloud space is valid, return * {@link org.eclipse.core.runtime.Status#OK_STATUS}. * @param cloudServerURL target server URL containing the selected cloud * space. Used to check if other existing server instances with that server * URL already target the selected org/space. If null, a check will be * performed against the delegate's associated cloud server. * @param selectionObj a potential space selection. * @return if valid, return * {@link org.eclipse.core.runtime.Status#OK_STATUS}. Otherwise return * appropriate error status. Must not be null. */ public IStatus validateSpaceSelection(String cloudServerURL, CloudSpace selectedCloudSpace) { String errorMessage = null; if (cloudServerURL == null) { cloudServerURL = getCloudServer().getUrl(); } if (selectedCloudSpace == null) { errorMessage = Messages.ERROR_INVALID_SPACE; } else if (cloudServerURL != null) { List<CloudFoundryServer> cloudServers = CloudServerUtil.getCloudServers(); if (cloudServers != null) { for (CloudFoundryServer cloudServer : cloudServers) { // Can ignore the cloud space check if the URL is different. if (cloudServerURL.equals(cloudServer.getUrl()) && matchesSpace(selectedCloudSpace, cloudServer.getCloudFoundrySpace())) { errorMessage = NLS.bind(Messages.ERROR_SERVER_INSTANCE_CLOUD_SPACE_EXISTS, cloudServer .getServer().getName(), selectedCloudSpace.getName()); break; } } } } return (errorMessage != null) ? CloudFoundryPlugin.getErrorStatus(errorMessage) : Status.OK_STATUS; } public CloudSpacesDescriptor getCurrentSpacesDescriptor() { return spacesDescriptor; } public void clearDescriptor() { spacesDescriptor = null; } /** * Resolves and validates the cloud org/space descriptor as well as the * cloud space selection in the current local server instance. Note that * cloud space descriptors are cached per each session of this Cloud Space * change handler, in order to prevent frequent requests for orgs and spaces * for credentials that have already been processed. To obtain a clean * updated list of descriptors, create a new cloud space change handler. * * @param urlText either correct URL or display version of the URL. If * display URL, attempt will be made to resolve the actual URL> * @param userName username to use to find list of spaces * @param password password to user to find list of spaces * @paramg selfSigned true if connecting to self-signed certificate. * @return descriptor with list of cloud spaces for the given URL and * credentials, or null if failed to resolve * @param context a runnable UI context, like a wizard. * @param updateDescriptor if true, will send a request to the server with * the given set of credentials to obtain a new list of orgs/spaces and then * perform check on the current server if a default space is set. If false, * will only perform a local space selection check on the current server * @throws CoreException if credentials and URL do not match the current * credentials and URL in the server, are invalid, or failed to retrieve * list of spaces */ public CloudSpacesDescriptor resolveDescriptor(String urlText, String userName, String password, boolean selfSigned, IRunnableContext context, boolean updateDescriptor) throws CoreException { CloudSpacesDescriptor descriptor = null; if (updateDescriptor) { descriptor = internalUpdateDescriptor(urlText, userName, password, selfSigned, context); } IStatus status = validateCurrent(getCurrentCloudSpace()); if (status != null && !status.isOK()) { throw new CoreException(status); } return descriptor; } protected CloudSpacesDescriptor internalUpdateDescriptor(String urlText, String userName, String password, boolean selfSigned, IRunnableContext context) throws CoreException { String actualURL = CloudUiUtil.getUrlFromDisplayText(urlText); validateCredentialsLocally(actualURL, userName, password); if (spacesDescriptor == null) { CloudOrgsAndSpaces orgsAndSpaces = CloudUiUtil.getCloudSpaces(userName, password, actualURL, true, selfSigned, context); if (orgsAndSpaces != null) { spacesDescriptor = new CloudSpacesDescriptor(orgsAndSpaces, userName, password, actualURL, selfSigned); } } return spacesDescriptor; } protected IStatus validateCurrent(CloudSpace currentSpace) { int severity = IStatus.OK; String validationMessage = Messages.VALID_ACCOUNT; CloudSpacesDescriptor descriptor = getCurrentSpacesDescriptor(); if (descriptor == null || descriptor.getOrgsAndSpaces() == null) { validationMessage = Messages.ERROR_CHECK_CONNECTION_NO_SPACES; severity = IStatus.ERROR; } else if (getSpaceWithNoServerInstance() == null) { validationMessage = Messages.ERROR_ALL_SPACES_ASSOCIATED_SERVER_INSTANCES; severity = IStatus.ERROR; } else { return validateSpaceSelection(currentSpace); } return CloudFoundryPlugin.getStatus(validationMessage, severity); } protected void validateCredentialsLocally(String url, String userName, String password) throws CoreException { String actualURL = cloudServer.getUrl(); String actualUserName = cloudServer.getUsername(); String actualPassword = cloudServer.getPassword(); boolean isValid = true; String[][] valuesToCheck = { { url, actualURL }, { userName, actualUserName }, { password, actualPassword } }; for (String[] value : valuesToCheck) { if (!areValid(value[0], value[1])) { isValid = false; break; } } if (!isValid) { throw new CoreException(CloudFoundryPlugin.getErrorStatus(NLS.bind( Messages.CloudSpacesDelegate_ERROR_FAIL_LOADING_CLOUDSPACES, new String[] { actualURL, userName }))); } } /** * Both values must be non-null and equal to be valid. * @param expected * @param actual * @return true if valid, false otherwise. */ protected boolean areValid(String expected, String actual) { return actual != null ? actual.equals(expected) : false; } protected CloudFoundryServer getCloudServer() { return cloudServer; } public static boolean matchesSpace(CloudSpace selectedCloudSpace, CloudFoundrySpace existingSpace) { return (existingSpace == null && selectedCloudSpace == null) || (existingSpace != null && selectedCloudSpace != null && existingSpace.getOrgName().equals(selectedCloudSpace.getOrganization().getName()) && existingSpace .getSpaceName().equals(selectedCloudSpace.getName())); } /** * Given space selection, determine if it is valid. For example, a user * wishes to create a server instance to the selected cloudSpace, if the * cloud space is valid, return * {@link org.eclipse.core.runtime.Status#OK_STATUS}. * @param selectionObj a potential space selection. * @return if valid, return * {@link org.eclipse.core.runtime.Status#OK_STATUS}. Otherwise return * appropriate error status. Must not be null. */ public IStatus validateSpaceSelection(CloudSpace selectedCloudSpace) { return validateSpaceSelection(null, selectedCloudSpace); } public boolean hasSpace() { return getCurrentCloudSpace() != null; } /** * Sets a cloud space, selected externally. For example, a UI component may * invoke this method when a user selects a cloud space in the UI. * Subclasses can decide what to do when the UI component invokes this * method, like for example, setting the cloud space in a * {@link CloudFoundryServer} * @param selectedCloudSpace a selected space to be set. */ public abstract void setSelectedSpace(CloudSpace selectedCloudSpace); /** * * @return currently set cloud space, or null. */ protected abstract CloudSpace getCurrentCloudSpace(); }