/*******************************************************************************
* 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();
}