/******************************************************************************* * Copyright (c) May 18, 2011 Zend Technologies Ltd. * 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 *******************************************************************************/ package org.zend.sdklib.manager; import java.io.IOException; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.net.UnknownHostException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import org.zend.sdklib.internal.library.AbstractChangeNotifier; import org.zend.sdklib.internal.target.UserBasedTargetLoader; import org.zend.sdklib.internal.target.ZendTarget; import org.zend.sdklib.internal.target.ZendTargetAutoDetect; import org.zend.sdklib.target.ITargetLoader; import org.zend.sdklib.target.IZendTarget; import org.zend.sdklib.target.LicenseExpiredException; import org.zend.webapi.core.WebApiException; import org.zend.webapi.core.connection.data.values.ServerType; import org.zend.webapi.core.connection.data.values.WebApiVersion; import org.zend.webapi.core.connection.data.values.ZendServerVersion; import org.zend.webapi.core.connection.response.ResponseCode; import org.zend.webapi.core.progress.BasicStatus; import org.zend.webapi.core.progress.StatusCode; /** * Target environments manager for the This is a thread-safe class that can be * used across threads * * @author Roy, 2011 */ public class TargetsManager extends AbstractChangeNotifier { public static final String DEFAULT_KEY = "sdk"; //$NON-NLS-1$ /** * All targets loaded in the manager */ private List<IZendTarget> all = new ArrayList<IZendTarget>(1); /** * The mechanism that is responsible to load the targets */ private final ITargetLoader loader; /** * Default target Id (for fast execution) */ private String defaultId = null; public TargetsManager() { this(new UserBasedTargetLoader()); } public TargetsManager(ITargetLoader loader) { this.loader = loader; load(); } public void reload() { this.all.clear(); load(); } /** * @param target * @return * @throws LicenseExpiredException * @throws WebApiException */ public synchronized IZendTarget add(IZendTarget target) throws TargetException, LicenseExpiredException { return add(target, false); } /** * @param target * @param suppressConnect * @return * @throws LicenseExpiredException * @throws WebApiException */ public synchronized IZendTarget add(IZendTarget target, boolean suppressConnect) throws TargetException, LicenseExpiredException { if (!validTarget(target)) { return null; } // try to connect to server try { if (!suppressConnect && !target.connect()) { return null; } } catch (WebApiException e) { throw new TargetException(e); } catch (LicenseExpiredException e) { throw new TargetException(e); } IZendTarget existingTarget = getTarget(target.getHost(), target.getKey()); if (existingTarget != null) { return updateTarget(existingTarget, target, suppressConnect); } else { //check whether the target descriptor exists //it could be added by another studio installation if(this.loader.isAvailable(target)) this.loader.update(target); else this.loader.add(target); // adds the target to the list final boolean added = this.all.add(target); if (this.all.size() == 1) { defaultId = this.all.get(0).getId(); } if (added) { statusChanged(new BasicStatus(StatusCode.UNKNOWN, "added target", "added target")); } return added ? target : null; } } private IZendTarget getTarget(URL host, String key) { for (IZendTarget t : all) { try { if (host.toURI().equals(t.getHost().toURI()) && key.equals(t.getKey())) { return t; } } catch (URISyntaxException e) { // ignore } } return null; } public synchronized IZendTarget remove(IZendTarget target) { if (target == null) { throw new IllegalArgumentException("Target cannot be null"); } if (!this.all.contains(target)) { throw new IllegalArgumentException(MessageFormat.format("Target with id ''{0}'' does not exist.", target.getId())); } //check whether the target descriptor still exist //it could be deleted by another studio installation if(this.loader.isAvailable(target)) this.loader.remove(target); // remove the specified target final boolean removed = this.all.remove(target); if (this.all.size() == 0) { defaultId = null; } if (removed) { statusChanged(new BasicStatus(StatusCode.UNKNOWN, "removed target", "removed target")); } return removed ? target : null; } /** * Remove all temporary targets. Target is temporary when * {@link IZendTarget#isTemporary()} returns <code>true</code>. */ public void removeAllTemporary() { List<IZendTarget> toRemove = new ArrayList<IZendTarget>(); for (IZendTarget target : all) { if (target.isTemporary()) { toRemove.add(target); } } for (IZendTarget target : toRemove) { remove(target); } } /** * Finds a target given target id * * @param i * @return the specified target */ public synchronized IZendTarget getTargetById(String id) { if (id == null) { return null; } for (IZendTarget target : getTargets()) { if (id.equals(target.getId())) { return target; } } return null; } public synchronized IZendTarget detectLocalhostTarget(String targetId, String key, String secretKey) throws DetectionException, LicenseExpiredException { return detectLocalhostTarget(targetId, key, secretKey, true, true); } /** * Returns a target that represents the localhost zend server. Returned * target may be not fully initialized, requiring some extra commands. * Clients should use {@link IZendTarget#isTemporary()} to test whether they * can connect safely. * * @param targetId * target id to use, null if not specified * @param key * key to use, null if not specified * @return the detected localhost target, or null if detection failed * @throws DetectionException * @throws LicenseExpiredException */ public synchronized IZendTarget detectLocalhostTarget(String targetId, String key) throws DetectionException, LicenseExpiredException { return detectLocalhostTarget(targetId, key, true, true); } /** * * @param targetId * target id to use * @param key * key name to use * @param add * whether to add found target to targets list * @param createKey * whether to attempt to automatically generate key secret in * server config file * @return * @throws DetectionException * @throws LicenseExpiredException */ public synchronized IZendTarget detectLocalhostTarget(String targetId, String key, boolean add, boolean createKey) throws DetectionException, LicenseExpiredException { return detectLocalhostTarget(targetId, key, null, add, createKey); } /** * * @param targetId * target id to use * @param key * key name to use * @param add * whether to add found target to targets list * @param createKey * whether to attempt to automatically generate key secret in * server config file * @return * @throws DetectionException * @throws LicenseExpiredException */ public synchronized IZendTarget detectLocalhostTarget(String targetId, String key, String secretKey, boolean add, boolean createKey) throws DetectionException, LicenseExpiredException { if (targetId == null) { targetId = createUniqueId(null); } key = key != null ? key : DEFAULT_KEY + "." //$NON-NLS-1$ + System.getProperty("user.name"); //$NON-NLS-1$ final IZendTarget existing = getExistingLocalhost(); ZendTargetAutoDetect detection = getAutoDetector(); String existingSecret = null; if (secretKey != null) { existingSecret = secretKey; } else { try { existingSecret = detection.findExistingSecretKey(key); } catch (IOException e) { } } // only return existing, if it's key still exists and is valid if ((existing != null) && (existingSecret != null) && existingSecret.equals(existing.getSecretKey())) { log.info(MessageFormat .format("Local target {0} has been found with id {1}. ", existing.getHost(), existing.getId())); return existing; } // if there's no key in server config and we don't want to create // automatic one, throw an error if ((existingSecret == null) && (!createKey)) { throw new DetectionException("Key entry '" + key + "' not found."); } IZendTarget local = null; try { // localhost not found - create one if (secretKey != null) { local = detection.createLocalhostTarget(targetId, key, secretKey); } else { local = detection.createLocalhostTarget(targetId, key); } if (add) { return add(local); } else { return local; } } catch (IOException e) { log.warning(e); throw new PrivilegesException(e.getMessage()); } catch (TargetException e) { Throwable cause = e.getCause(); if (cause instanceof WebApiException) { WebApiException webE = (WebApiException) cause; final ResponseCode responseCode = webE.getResponseCode(); if (local != null && (responseCode == null || responseCode == ResponseCode.UNKNOWN_METHOD || responseCode == ResponseCode.PAGE_NOT_FOUND)) { // try to repeat for zs6 try { local.connect(WebApiVersion.V1_3, ServerType.ZEND_SERVER); if (add) { return add(local, true); } else { return local; } } catch (WebApiException e1) { final ResponseCode rCode = e1.getResponseCode(); int code = (rCode != null) ? rCode.getCode() : -1; final String message = e1.getMessage(); throw new ServerVersionException(code, message); } catch (TargetException e1) { throw new DetectionException(e1); } catch (LicenseExpiredException e1) { throw e1; } } if (cause instanceof WebApiException) { int code = (responseCode != null) ? responseCode.getCode() : -1; final String message = webE.getMessage(); throw new ServerVersionException(code, message); } else { throw new DetectionException(e); } } return null; } } public ZendTargetAutoDetect getAutoDetector() { return new ZendTargetAutoDetect(); } public synchronized String applyKeyToLocalhost(String key, String secretKey) throws IOException { final ZendTargetAutoDetect detection = getAutoDetector(); final String appliedSecretKey = detection .applySecretKey(key, secretKey); return appliedSecretKey; } public IZendTarget getExistingLocalhost() { final IZendTarget[] list = getTargets(); for (IZendTarget t : list) { if (ZendTargetAutoDetect.localhost.equals(t.getHost())) { return t; } } return null; } public synchronized IZendTarget[] getTargets() { return (IZendTarget[]) this.all .toArray(new ZendTarget[this.all.size()]); } /** * Creates and adds new target based on provided parameters. * * @param host * @param key * @param secretKey * @return * @throws LicenseExpiredException */ public IZendTarget createTarget(String host, String key, String secretKey) throws LicenseExpiredException { return createTarget(createUniqueId(null), host, key, secretKey); } /** * @param targetId * @param host * @param key * @param secretKey * @return * @throws LicenseExpiredException */ public IZendTarget createTarget(String targetId, String host, String key, String secretKey) throws LicenseExpiredException { return createTarget(targetId, host, key, secretKey, null); } /** * Creates and adds new target based on provided parameters. * * @param targetId * @param host * @param key * @param secretKey * @return * @throws LicenseExpiredException */ public IZendTarget createTarget(String targetId, String host, String key, String secretKey, Properties extraProperties) throws LicenseExpiredException { try { final ZendTarget t = new ZendTarget(targetId, new URL(host), key, secretKey); if (extraProperties != null && !extraProperties.isEmpty()) { final Set<Entry<Object, Object>> entrySet = extraProperties .entrySet(); for (Entry<Object, Object> entry : entrySet) { t.addProperty(entry.getKey().toString(), entry.getValue() .toString()); } } IZendTarget target = add(t); if (target == null) { return null; } return target; } catch (MalformedURLException e) { log.error("Error adding Zend Target " + targetId); log.error("\tPossible error: " + e.getMessage()); } catch (TargetException e) { log.error("Error adding Zend Target " + targetId); log.error("\tPossible error: " + e.getMessage()); } return null; } private IZendTarget updateTarget(IZendTarget existing, IZendTarget newTarget, boolean suppressConnect) throws LicenseExpiredException { IZendTarget updated = updateTarget(existing.getId(), newTarget .getHost().toString(), newTarget.getDefaultServerURL() .toString(), newTarget.getKey(), newTarget.getSecretKey(), suppressConnect); ZendTarget updatedZT = (ZendTarget) updated; ZendTarget newZT = (ZendTarget) newTarget; String[] newZTKeys = newZT.getPropertiesKeys(); for (String key : newZTKeys) { updatedZT.addProperty(key, newZT.getProperty(key)); } return updated; } public IZendTarget updateTarget(String targetId, String host, String defaultServer, String key, String secretKey) throws LicenseExpiredException { return updateTarget(targetId, host, defaultServer, key, secretKey, false); } public IZendTarget updateTarget(String targetId, String host, String defaultServer, String key, String secretKey, boolean suppressConnect) throws LicenseExpiredException { ZendTarget target = (ZendTarget) getTargetById(targetId); if (target == null) { log.info("Target with id '" + targetId + "' does not exist."); return null; } try { if (host != null) { target.setHost(new URL(host)); } if (defaultServer != null) { target.setDefaultServerURL(new URL(defaultServer)); } if (key != null) { target.setKey(key); } if (secretKey != null) { target.setSecretKey(secretKey); } try { if (!suppressConnect && !target.connect(WebApiVersion.V1_3, ServerType.ZEND_SERVER)) { return null; } } catch (WebApiException e) { try { if (!target.connect(WebApiVersion.UNKNOWN, ServerType.ZEND_SERVER)) { return null; } } catch (WebApiException ex) { if (!target.connect()) { return null; } } } IZendTarget updated = loader.update(target); if (updated != null) { statusChanged(new BasicStatus(StatusCode.UNKNOWN, "updated target", "updated target")); } return updated; } catch (MalformedURLException e) { log.error(e); } catch (WebApiException e) { log.error("Error during updating Zend Target with id '" + targetId + "'"); log.error("\tPossible error: " + e.getMessage()); } catch (LicenseExpiredException e) { throw e; } return null; } public IZendTarget updateTarget(IZendTarget target, boolean suppressConnect) { try { if (!suppressConnect && !target.connect()) { return null; } IZendTarget updated = loader.update(target); if (updated != null) { IZendTarget old = getTargetById(updated.getId()); int index = all.indexOf(old); all.remove(index); all.add(index, updated); statusChanged(new BasicStatus(StatusCode.UNKNOWN, "updated target", "updated target")); } return updated; } catch (WebApiException e) { log.error("Error during updating Zend Target with id '" + target.getId() + "'"); log.error("\tPossible error: " + e.getMessage()); } catch (LicenseExpiredException e) { log.error("Error during updating Zend Target with id '" + target.getId() + "'"); log.error("\tPossible error: " + e.getMessage()); } return null; } public IZendTarget updateTarget(IZendTarget target) { return updateTarget(target, false); } /** * Check for conflicts and errors in new target * * @param target * @return */ private boolean validTarget(IZendTarget target) { if (target == null) { log.error(new IllegalArgumentException("Target cannot be null.")); return false; } if (target.getId() == null) { log.error(new IllegalArgumentException( "Target is not valid. Target id cannot be null.")); return false; } IZendTarget dupTarget = getTargetById(target.getId()); if (dupTarget != null) { if (!dupTarget.isTemporary()) { log.error("Target with id '" + target.getId() + "' already exists."); return false; } else { remove(dupTarget); } } return true; } /** * @return the default target id if exists. null if no default is assigned */ public synchronized String getDefaultTargetId() { if (all.size() == 1) { return all.get(0).getId(); } return defaultId; } /** * Creates new target id unique in target manager. * * @param prefix * Optional prefix for generated id. Might be null. * * @return unique id. */ public String createUniqueId(String prefix) { if (prefix == null) { prefix = ""; //$NON-NLS-1$ } int idgenerator = getTargets().length; String id; do { id = prefix + Integer.toString(idgenerator++); } while (!isIdAvailable(id)); return id; } /** * Allows to check if specified target has exact Zend Server version. * * @param target * @return <code>true</code> version matches; otherwise return * <code>false</code> */ public static boolean checkExactVersion(IZendTarget target, ZendServerVersion expectedVersion) { if (target != null) { ZendServerVersion version = ZendServerVersion.byName(target .getProperty(IZendTarget.SERVER_VERSION)); return version.compareTo(expectedVersion) == 0; } return false; } /** * Allows to check if specified target has minimal accepted Zend Server * version. * * @param target * @return <code>true</code> version matches; otherwise return * <code>false</code> */ public static boolean checkMinVersion(IZendTarget target, ZendServerVersion minVersion) { if (target != null) { ZendServerVersion version = ZendServerVersion.byName(target .getProperty(IZendTarget.SERVER_VERSION)); return version.compareTo(minVersion) >= 0; } return false; } /** * Allows to check if specified target has maximal accepted Zend Server * version. * * @param target * @return <code>true</code> version matches; otherwise return * <code>false</code> */ public static boolean checkMaxVersion(IZendTarget target, ZendServerVersion maxVersion) { if (target != null) { ZendServerVersion version = ZendServerVersion.byName(target .getProperty(IZendTarget.SERVER_VERSION)); return version.compareTo(maxVersion) <= 0; } return false; } public static boolean isLocalhost(IZendTarget target) { return target != null && isLocalhost(target.getHost().getHost()); } public static boolean isLocalhost(String host) { if (host == null) { return false; } if ("localhost".equals(host) || "127.0.0.1".equals(host)) { //$NON-NLS-1$ //$NON-NLS-2$ return true; } try { return InetAddress.getLocalHost().getHostAddress().equals(host); } catch (UnknownHostException e) { // skip and continue } return false; } protected void load() { final IZendTarget[] loadAll = loader.loadAll(); for (IZendTarget zTarget : loadAll) { if (!validTarget(zTarget)) { log.error(new IllegalArgumentException( "Conflict found when adding " + zTarget.getId())); } else { this.all.add(zTarget); } } if (this.all.size() > 0) { defaultId = this.all.get(0).getId(); } } private boolean isIdAvailable(String id) { IZendTarget[] targets = getTargets(); for (IZendTarget target : targets) { String targetId = target.getId(); if (targetId.equals(id) || targetId.startsWith(id + "_")) { return false; } } return true; } }