/* * Copyright 2013 Hewlett-Packard Development Company, L.P * * Licensed under 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. */ package com.hp.alm.ali.idea.rest; import com.hp.alm.ali.idea.cfg.AliProjectConfigurable; import com.hp.alm.ali.idea.cfg.AuthenticationFailed; import com.hp.alm.ali.idea.services.WeakListeners; import com.hp.alm.ali.idea.cfg.AliConfigurable; import com.hp.alm.ali.idea.cfg.AliConfiguration; import com.hp.alm.ali.idea.cfg.AliProjectConfiguration; import com.hp.alm.ali.idea.cfg.ConfigurationListener; import com.hp.alm.ali.idea.model.ServerStrategy; import com.hp.alm.ali.rest.client.AliRestClientFactory; import com.hp.alm.ali.rest.client.RestClient; import com.hp.alm.ali.rest.client.RestClientFactory; import com.hp.alm.ali.rest.client.ResultInfo; import com.hp.alm.ali.rest.client.exception.AuthenticationFailureException; import com.intellij.ide.BrowserUtil; import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.util.net.HttpConfigurable; import com.intellij.util.net.IdeaWideProxySelector; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.TestOnly; import javax.swing.event.HyperlinkEvent; import java.io.InputStream; import java.net.InetSocketAddress; import java.net.PasswordAuthentication; import java.net.Proxy; import java.util.List; public class RestService implements ConfigurationListener { private ServerType serverType = ServerType.NONE; volatile private RestClient restClient; private WeakListeners<RestListener> listeners; private WeakListeners<ServerTypeListener> serverTypeListeners; private Project project; private AliProjectConfiguration projConf; private RestServiceLogger restServiceLogger; final Notification errorNotification; private static RestClientFactory factory = AliRestClientFactory.getInstance(); public RestService(final Project project, TroubleShootService troubleShootService, AliProjectConfiguration conf) { this.project = project; this.restServiceLogger = troubleShootService; this.projConf = conf; listeners = new WeakListeners<RestListener>(); serverTypeListeners = new WeakListeners<ServerTypeListener>(); conf.addListener(this); ApplicationManager.getApplication().getComponent(AliConfiguration.class).addListener(this); errorNotification = new Notification("HP ALM Integration", "Cannot connect to HP ALM", "<p><a href=\"\">Configure HP ALM integration ...</a></p>", NotificationType.ERROR, new NotificationListener() { public void hyperlinkUpdate(Notification notification, HyperlinkEvent event) { notification.expire(); ShowSettingsUtil.getInstance().showSettingsDialog(project, AliProjectConfigurable.DISPLAY_NAME); } }); } public void launchProjectUrl(String href) { String query = href.contains("?")? "&": "?"; BrowserUtil.launchBrowser(projConf.getLocation() + "/rest/domains/" + projConf.getDomain() + "/projects/" + projConf.getProject() + "/" + href + query + "login-form-required=Y"); } private RestClient createRestClient(AliProjectConfiguration conf) { return createRestClient(conf.getLocation(), conf.getDomain(), conf.getProject(), conf.getUsername(), conf.getPassword(), RestClient.SessionStrategy.AUTO_LOGIN); } public static RestClient createRestClient(String location, String domain, String project, String username, String password, RestClient.SessionStrategy strategy) { RestClient restClient = factory.create(location, domain, project, username, password, strategy); restClient.setEncoding(null); restClient.setTimeout(10000); HttpConfigurable httpConfigurable = HttpConfigurable.getInstance(); IdeaWideProxySelector proxySelector = new IdeaWideProxySelector(httpConfigurable); List<Proxy> proxies = proxySelector.select(VfsUtil.toUri(location)); if (proxies != null) { for (Proxy proxy: proxies) { if (HttpConfigurable.isRealProxy(proxy) && Proxy.Type.HTTP.equals(proxy.type())) { InetSocketAddress address = (InetSocketAddress)proxy.address(); restClient.setHttpProxy(address.getHostName(), address.getPort()); // not sure how IdeaWideAuthenticator & co. is supposed to work, let's try something simple: if(httpConfigurable.PROXY_AUTHENTICATION) { PasswordAuthentication authentication = httpConfigurable.getPromptedAuthentication("HP ALI", address.getHostName()); if(authentication != null) { restClient.setHttpProxyCredentials(authentication.getUserName(), new String(authentication.getPassword())); } } break; } } } return restClient; } public synchronized RestClient getRestClient() { if(restClient == null) { restClient = createRestClient(projConf); } return restClient; } public int get(MyResultInfo result, String template, Object... params) { return execute(getRestClient(), project, restServiceLogger, new MyGetMethod(), null, result, template, params); } public int put(String xml, MyResultInfo result, String template, Object... params) { return put(new MyInputData(xml), result, template, params); } public int put(MyInputData inputData, MyResultInfo result, String template, Object... params) { return execute(getRestClient(), project, restServiceLogger, new MyPutMethod(), inputData, result, template, params); } public int post(MyInputData inputData, MyResultInfo result, String template, Object... params) { return execute(getRestClient(), project, restServiceLogger, new MyPostMethod(), inputData, result, template, params); } public int post(String xml, MyResultInfo result, String template, Object... params) { return post(new MyInputData(xml), result, template, params); } public int delete(MyResultInfo result, String template, Object... params) { return execute(getRestClient(), project, restServiceLogger, new MyDeleteMethod(), null, result, template, params); } public void delete(String template, Object... params) { execute(getRestClient(), project, restServiceLogger, new MyDeleteMethod(), null, new MyResultInfo(), template, params); } public static String getForString(RestClient restClient, String template, Object ... params) { TroubleShootService troubleShootService = ApplicationManager.getApplication().getComponent(TroubleShootService.class); MyResultInfo result = new MyResultInfo(); int status = execute(restClient, null, troubleShootService, new MyGetMethod(), null, result, template, params); if(status < 200 || status > 299) { throw new RestException(result); } return result.getBodyAsString(); } public InputStream getForStream(String template, Object ... params) { MyResultInfo result = new MyResultInfo(); int status = get(result, template, params); if(status < 200 || status > 299) { throw new RestException(result); } UIUtil.invokeLaterIfNeeded(new Runnable() { public void run() { errorNotification.expire(); } }); return result.getBodyAsStream(); } private static int execute(RestClient restClient, Project project, RestServiceLogger restServiceLogger, MyMethod method, MyInputData myInput, MyResultInfo myResult, String template, Object... params) { ResultInfo info = ResultInfo.create(myResult.getOutputStream()); long id = restServiceLogger.request(project, method.getName(), myInput, template, params); int code; try { code = method.execute(restClient, myInput == null? null: myInput.getInputData(), info, template, params); } catch(AuthenticationFailureException e) { restServiceLogger.loginFailure(id, e); throw e; } myResult.copyFrom(info); restServiceLogger.response(id, code, myResult); return code; } public void addListener(RestListener listener) { listeners.add(listener); } public void removeListener(RestListener listener) { listeners.remove(listener); } public void onChanged() { try { synchronized (this) { if(restClient != null) { logout(restClient); } serverType = ServerType.NONE; // fire event later (when connecting) restClient = createRestClient(projConf); } fireRestConfigurationChanged(); } finally { checkConnectivity(); } } public static void logout(final RestClient client) { ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { public void run() { client.logout(); } }); } private void fireRestConfigurationChanged() { listeners.fire(new WeakListeners.Action<RestListener>() { public void fire(RestListener listener) { listener.restConfigurationChanged(); } }); } public void checkConnectivity() { ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { public void run() { try { if(setConnectingType()) { setServerType(AliConfigurable.getServerType(getRestClient(), false)); } } catch(Exception e) { if(e instanceof AuthenticationFailed && projConf.getPassword().isEmpty()) { setServerType(ServerType.NEEDS_PASSWORD); } else { setServerType(ServerType.NONE); } UIUtil.invokeLaterIfNeeded(new Runnable() { public void run() { ToolWindow toolWindow = project.getComponent(ToolWindowManager.class).getToolWindow("HP ALI"); if (toolWindow != null && toolWindow.getContentManager().getContentCount() > 1) { expireConnectivityError(); Notifications.Bus.notify(errorNotification, project); } } }); } } }); } public void expireConnectivityError() { errorNotification.expire(); } public synchronized ServerType getServerTypeIfAvailable() { return serverType; } public synchronized ServerType getServerType() throws InterruptedException { while(serverType == ServerType.CONNECTING) { wait(); } return serverType; } public synchronized ServerStrategy getServerStrategy() { return project.getComponent(serverType.getClazz()); } private boolean setConnectingType() { synchronized (this) { if(serverType != ServerType.NONE) { return false; } serverType = ServerType.CONNECTING; notifyAll(); } fireServerTypeEvent(); return true; } public void setServerType(ServerType serverType) { if (serverType.isConnected()) { // perform additional setup (e.g. workspace selection) project.getComponent(serverType.getClazz()).beforeConnectionHandler(); } synchronized (this) { this.serverType = serverType; notifyAll(); } fireServerTypeEvent(); } public void fireServerTypeEvent() { serverTypeListeners.fire(new WeakListeners.Action<ServerTypeListener>() { public void fire(ServerTypeListener listener) { listener.connectedTo(serverType); } }); } public void addServerTypeListener(ServerTypeListener listener) { addServerTypeListener(listener, true); } public void addServerTypeListener(ServerTypeListener listener, boolean weak) { serverTypeListeners.add(listener, weak); } public void removeServerTypeListener(ServerTypeListener listener) { serverTypeListeners.remove(listener); } public boolean _isRegistered(ServerTypeListener listener) { return serverTypeListeners.isRegistered(listener); } @TestOnly static void _setFactory(RestClientFactory factory) { RestService.factory = factory; } @TestOnly void _setRestClient(RestClient restClient) { this.restClient = restClient; } @TestOnly void _setRestServiceLogger(RestServiceLogger logger) { this.restServiceLogger = logger; } @TestOnly synchronized void _setServerType(ServerType serverType) { this.serverType = serverType; notifyAll(); } }