diff --git a/platform/remote-servers/api/src/com/intellij/remoteServer/runtime/clientLibrary/ClientLibraryDescription.java b/platform/remote-servers/api/src/com/intellij/remoteServer/runtime/clientLibrary/ClientLibraryDescription.java new file mode 100644 index 000000000000..2f701488abd3 --- /dev/null +++ b/platform/remote-servers/api/src/com/intellij/remoteServer/runtime/clientLibrary/ClientLibraryDescription.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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.intellij.remoteServer.runtime.clientLibrary; + +import org.jetbrains.annotations.NotNull; + +import java.net.URL; + +/** + * @author nik + */ +public class ClientLibraryDescription { + private final String myId; + @NotNull private final URL myDescriptionUrl; + + public ClientLibraryDescription(@NotNull String id, @NotNull URL descriptionUrl) { + myId = id; + myDescriptionUrl = descriptionUrl; + } + + public final String getId() { + return myId; + } + + @NotNull + public URL getDescriptionUrl() { + return myDescriptionUrl; + } +} diff --git a/platform/remote-servers/api/src/com/intellij/remoteServer/runtime/clientLibrary/ClientLibraryManager.java b/platform/remote-servers/api/src/com/intellij/remoteServer/runtime/clientLibrary/ClientLibraryManager.java new file mode 100644 index 000000000000..6969d144cb26 --- /dev/null +++ b/platform/remote-servers/api/src/com/intellij/remoteServer/runtime/clientLibrary/ClientLibraryManager.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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.intellij.remoteServer.runtime.clientLibrary; + +import com.intellij.execution.configurations.RuntimeConfigurationError; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.io.File; +import java.util.EventListener; +import java.util.List; + +/** + * @author nik + */ +public abstract class ClientLibraryManager { + @NotNull + public static ClientLibraryManager getInstance() { + return ServiceManager.getService(ClientLibraryManager.class); + } + + public abstract boolean download(@NotNull ClientLibraryDescription description); + + @NotNull + public abstract List getLibraries(@NotNull ClientLibraryDescription description); + + /** + * @deprecated to be removed with obsolete cloud implementations + */ + public abstract void addListener(@NotNull CloudClientLibraryManagerListener listener, @NotNull Disposable disposable); + + /** + * @deprecated will be pushed down to implementation when obsolete cloud implementations are removed + */ + public abstract boolean isDownloaded(@NotNull ClientLibraryDescription description); + + /** + * @deprecated to be removed with obsolete cloud implementations + */ + public abstract void checkConfiguration(@NotNull ClientLibraryDescription description, @Nullable Project project, + @Nullable JComponent component) throws RuntimeConfigurationError; + + /** + * @deprecated will be pushed down to implementation when obsolete cloud implementations are removed + */ + public abstract void download(@NotNull ClientLibraryDescription description, @Nullable Project project, @Nullable JComponent component); + + public interface CloudClientLibraryManagerListener extends EventListener { + void downloaded(); + } +} diff --git a/platform/remote-servers/impl/src/META-INF/RemoteServers.xml b/platform/remote-servers/impl/src/META-INF/RemoteServers.xml index 5c5e9682f0c6..edb1533f4ebb 100644 --- a/platform/remote-servers/impl/src/META-INF/RemoteServers.xml +++ b/platform/remote-servers/impl/src/META-INF/RemoteServers.xml @@ -12,6 +12,8 @@ serviceImplementation="com.intellij.remoteServer.impl.runtime.ServerConnectionManagerImpl"/> + { + private static final Logger LOG = Logger.getInstance(ClientLibraryManagerImpl.class); + private Map> myFiles = new LinkedHashMap>(); + + private EventDispatcher myEventDispatcher + = EventDispatcher.create(CloudClientLibraryManagerListener.class); + + + @Override + public void addListener(@NotNull CloudClientLibraryManagerListener listener, @NotNull Disposable disposable) { + myEventDispatcher.addListener(listener, disposable); + } + + public State getState() { + State result = new State(); + for (Map.Entry> entry : myFiles.entrySet()) { + DownloadedLibraryState libraryState = new DownloadedLibraryState(); + libraryState.myId = entry.getKey(); + for (File file : entry.getValue()) { + libraryState.myPaths.add(FileUtil.toSystemIndependentName(file.getAbsolutePath())); + } + result.myLibraries.add(libraryState); + } + return result; + } + + @Override + public void loadState(State state) { + myFiles = new HashMap>(); + for (DownloadedLibraryState libraryState : state.myLibraries) { + List files = new ArrayList(); + for (String path : libraryState.myPaths) { + files.add(new File(path)); + } + myFiles.put(libraryState.myId, files); + } + } + + @Override + public boolean isDownloaded(@NotNull ClientLibraryDescription description) { + List files = myFiles.get(description.getId()); + if (files == null || files.isEmpty()) { + return false; + } + for (File file : files) { + if (!file.exists()) { + return false; + } + } + return true; + } + + @Override + public void checkConfiguration(@NotNull final ClientLibraryDescription description, final @Nullable Project project, + final @Nullable JComponent component) throws RuntimeConfigurationError { + if (!isDownloaded(description)) { + throw new RuntimeConfigurationError("Client libraries were not downloaded", new Runnable() { + @Override + public void run() { + download(description, project, component); + } + }); + } + } + + private static File getStoreDirectory(ClientLibraryDescription description) { + return new File(PathManager.getSystemPath(), "remote-server-libraries/" + description.getId()); + } + + @NotNull + @Override + public List getLibraries(@NotNull ClientLibraryDescription description) { + File[] files = getStoreDirectory(description).listFiles(); + return files == null ? Collections.emptyList() : Arrays.asList(files); + } + + @Override + public void download(@NotNull final ClientLibraryDescription libraryDescription, @Nullable Project project, @Nullable JComponent component) { + final DownloadableFileService downloadService = DownloadableFileService.getInstance(); + + final Ref descriptionRef = new Ref(); + ProgressManager.getInstance().runProcessWithProgressSynchronously( + new Runnable() { + + @Override + public void run() { + URL versionsUrl = libraryDescription.getDescriptionUrl(); + final DownloadableFileSetVersions versions = downloadService.createFileSetVersions(null, versionsUrl); + + final Semaphore semaphore = new Semaphore(); + semaphore.down(); + versions.fetchVersions(new DownloadableFileSetVersions.FileSetVersionsCallback() { + @Override + public void onSuccess(@NotNull List versions) { + if (versions.isEmpty()) { + LOG.error("No versions fetched"); + } + else { + descriptionRef.set(versions.get(0)); + } + semaphore.up(); + } + + @Override + public void onError(@NotNull String errorMessage) { + LOG.error(errorMessage); + semaphore.up(); + } + }); + semaphore.waitFor(); + } + }, "Fetching library description", false, project, component); + + if (descriptionRef.isNull()) { + return; + } + + DownloadableFileSetDescription description = descriptionRef.get(); + + FileDownloader downloader = downloadService.createDownloader(description, project, component); + downloader = downloader.toDirectory(getStoreDirectory(libraryDescription).getAbsolutePath()); + VirtualFile[] virtualFiles = downloader.download(); + if (virtualFiles == null || virtualFiles.length != description.getFiles().size()) { + return; + } + + List files = myFiles.get(libraryDescription.getId()); + if (files == null) { + files = new ArrayList(); + myFiles.put(libraryDescription.getId(), files); + } + for (VirtualFile file : virtualFiles) { + files.add(VfsUtilCore.virtualToIoFile(file)); + } + + myEventDispatcher.getMulticaster().downloaded(); + } + + @Override + public boolean download(@NotNull final ClientLibraryDescription description) { + if (isDownloaded(description)) { + return true; + } + + download(description, null, null); + + return isDownloaded(description); + } + + @Tag("client-library") + public static class DownloadedLibraryState { + @Attribute("id") + public String myId; + + @Property(surroundWithTag = false) + @AbstractCollection(surroundWithTag = false, elementTag = "file", elementValueAttribute = "path") + public List myPaths = new ArrayList(); + } + + public static class State { + @Property(surroundWithTag = false) + @AbstractCollection(surroundWithTag = false) + public List myLibraries = new ArrayList(); + } +}