IDEADEV-40127 CME: IdeaServerManagerImpl.processStorages + fix ComponentVersionsProce...
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / components / impl / stores / StateStorageManagerImpl.java
blobdffc16309cad3e047f10ec5d0275eb238d44a5a4
1 package com.intellij.openapi.components.impl.stores;
3 import com.intellij.openapi.Disposable;
4 import com.intellij.openapi.application.ex.ApplicationManagerEx;
5 import com.intellij.openapi.components.*;
6 import com.intellij.openapi.diagnostic.Logger;
7 import com.intellij.openapi.options.StreamProvider;
8 import com.intellij.openapi.util.Disposer;
9 import com.intellij.openapi.util.JDOMUtil;
10 import com.intellij.openapi.util.Pair;
11 import com.intellij.openapi.util.text.StringUtil;
12 import com.intellij.openapi.vfs.VirtualFile;
13 import com.intellij.util.containers.MultiMap;
14 import com.intellij.util.io.fs.IFile;
15 import org.jdom.Document;
16 import org.jdom.Element;
17 import org.jdom.JDOMException;
18 import org.jetbrains.annotations.NotNull;
19 import org.jetbrains.annotations.Nullable;
20 import org.picocontainer.MutablePicoContainer;
21 import org.picocontainer.PicoContainer;
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.net.ConnectException;
27 import java.util.*;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
31 public abstract class StateStorageManagerImpl implements StateStorageManager, Disposable, StreamProvider, ComponentVersionProvider {
33 private static final Logger LOG = Logger.getInstance("#" + StateStorageManagerImpl.class.getName());
35 private final Map<String, String> myMacros = new HashMap<String, String>();
36 private final Map<String, StateStorage> myStorages = new HashMap<String, StateStorage>();
37 private final Map<String, StateStorage> myPathToStorage = new HashMap<String, StateStorage>();
38 private final TrackingPathMacroSubstitutor myPathMacroSubstitutor;
39 private final String myRootTagName;
40 private Object mySession;
41 private final PicoContainer myPicoContainer;
43 private Map<String, Long> myComponentVersions;
44 private final Object myComponentVersLock = new Object();
46 private String myVersionsFilePath = null;
48 private final MultiMap<RoamingType, StreamProvider> myStreamProviders = new MultiMap<RoamingType, StreamProvider>();
50 public StateStorageManagerImpl(@Nullable final TrackingPathMacroSubstitutor pathMacroSubstitutor,
51 final String rootTagName,
52 @Nullable Disposable parentDisposable,
53 PicoContainer picoContainer) {
54 myPicoContainer = picoContainer;
55 myRootTagName = rootTagName;
56 myPathMacroSubstitutor = pathMacroSubstitutor;
57 if (parentDisposable != null) {
58 Disposer.register(parentDisposable, this);
62 public synchronized void addMacro(String macro, String expansion) {
63 myMacros.put("$" + macro + "$", expansion);
66 @Nullable
67 public StateStorage getStateStorage(@NotNull final Storage storageSpec) throws StateStorage.StateStorageException {
68 final String key = getStorageSpecId(storageSpec);
69 return getStateStorage(storageSpec, key);
72 @Nullable
73 private StateStorage getStateStorage(final Storage storageSpec, final String key) throws StateStorage.StateStorageException {
74 if (myStorages.get(key) == null) {
75 final StateStorage stateStorage = createStateStorage(storageSpec);
76 putStorageToMap(key, stateStorage);
79 return myStorages.get(key);
82 @Nullable
83 public StateStorage getFileStateStorage(final String fileName) {
84 if (myStorages.get(fileName) == null) {
85 final StateStorage stateStorage = createFileStateStorage(fileName);
86 putStorageToMap(fileName, stateStorage);
89 return myStorages.get(fileName);
92 public Collection<String> getStorageFileNames() {
93 return Collections.unmodifiableCollection(myStorages.keySet());
96 private void putStorageToMap(final String key, final StateStorage stateStorage) {
97 if (stateStorage != null) {
98 if (stateStorage instanceof FileBasedStorage) {
99 //fixing problem with 2 macros for the same directory (APP_CONFIG and OPTIONS)
100 String filePath = ((FileBasedStorage)stateStorage).getFilePath();
101 if (myPathToStorage.containsKey(filePath)) {
102 StateStorage existing = myPathToStorage.get(filePath);
103 myStorages.put(key, existing);
105 else {
106 myPathToStorage.put(filePath, stateStorage);
107 myStorages.put(key, stateStorage);
110 else {
111 myStorages.put(key, stateStorage);
117 public long getVersion(String name) {
118 Map<String, Long> versions = getComponentVersions();
119 return versions.containsKey(name) ? versions.get(name).longValue() : 0;
122 public void changeVersionsFilePath(String newPath) {
123 myVersionsFilePath = newPath;
124 resetLocalVersions();
127 public void resetLocalVersions(){
128 synchronized (myComponentVersLock) {
129 myComponentVersions = null;
133 private Map<String, Long> loadVersions() {
134 if (myVersionsFilePath == null) {
135 myVersionsFilePath = getVersionsFilePath();
138 TreeMap<String, Long> result = new TreeMap<String, Long>();
139 String filePath = getNotNullVersionsFilePath();
140 if (filePath != null) {
141 try {
142 Document document = JDOMUtil.loadDocument(new File(filePath));
143 loadComponentVersions(result, document);
145 catch (JDOMException e) {
146 LOG.debug(e);
148 catch (IOException e) {
149 LOG.debug(e);
152 return result;
155 private String getNotNullVersionsFilePath() {
156 if (myVersionsFilePath == null) {
157 myVersionsFilePath = getVersionsFilePath();
160 return myVersionsFilePath;
163 public static void loadComponentVersions(Map<String, Long> result, Document document) {
164 List componentObjs = document.getRootElement().getChildren("component");
165 for (Object componentObj : componentObjs) {
166 if (componentObj instanceof Element) {
167 Element componentEl = (Element)componentObj;
168 String name = componentEl.getAttributeValue("name");
169 String version = componentEl.getAttributeValue("version");
171 if (name != null && version != null) {
172 try {
173 result.put(name, Long.parseLong(version));
175 catch (NumberFormatException e) {
176 //ignore
183 protected abstract String getVersionsFilePath();
185 public void changeVersion(String name, long version) {
186 getComponentVersions().put(name, version);
189 @Nullable
190 private StateStorage createStateStorage(final Storage storageSpec) throws StateStorage.StateStorageException {
191 if (!storageSpec.storageClass().equals(StorageAnnotationsDefaultValues.NullStateStorage.class)) {
192 final String key = UUID.randomUUID().toString();
193 ((MutablePicoContainer)myPicoContainer).registerComponentImplementation(key, storageSpec.storageClass());
195 return (StateStorage)myPicoContainer.getComponentInstance(key);
197 else if (!storageSpec.stateSplitter().equals(StorageAnnotationsDefaultValues.NullStateSplitter.class)) {
198 return createDirectoryStateStorage(storageSpec.file(), storageSpec.stateSplitter());
200 else {
201 return createFileStateStorage(storageSpec.file());
205 private static String getStorageSpecId(final Storage storageSpec) {
206 if (!storageSpec.storageClass().equals(StorageAnnotationsDefaultValues.NullStateStorage.class)) {
207 return storageSpec.storageClass().getName();
209 else {
210 return storageSpec.file();
214 public void clearStateStorage(@NotNull final String file) {
215 myStorages.remove(file);
218 @Nullable
219 private StateStorage createDirectoryStateStorage(final String file, final Class<? extends StateSplitter> splitterClass)
220 throws StateStorage.StateStorageException {
221 final String expandedFile = expandMacroses(file);
222 if (expandedFile == null) {
223 myStorages.put(file, null);
224 return null;
227 final StateSplitter splitter;
229 try {
230 splitter = splitterClass.newInstance();
232 catch (InstantiationException e) {
233 throw new StateStorage.StateStorageException(e);
235 catch (IllegalAccessException e) {
236 throw new StateStorage.StateStorageException(e);
239 return new DirectoryBasedStorage(myPathMacroSubstitutor, expandedFile, splitter, this, myPicoContainer);
242 @Nullable
243 StateStorage createFileStateStorage(@NotNull final String fileSpec) {
244 String expandedFile = expandMacroses(fileSpec);
245 if (expandedFile == null) {
246 myStorages.put(fileSpec, null);
247 return null;
250 return createFileStateStorage(fileSpec, expandedFile, myRootTagName, myPicoContainer);
253 protected StateStorage createFileStateStorage(final String fileSpec, final String expandedFile, final String rootTagName,
254 final PicoContainer picoContainer) {
255 return new FileBasedStorage(getMacroSubstitutor(fileSpec), this, expandedFile, fileSpec, rootTagName, this, picoContainer,
256 ComponentRoamingManager.getInstance(), this) {
257 @NotNull
258 protected StorageData createStorageData() {
259 return StateStorageManagerImpl.this.createStorageData(fileSpec);
264 public void saveContent(final String fileSpec, final InputStream content, final long size, final RoamingType roamingType, boolean async) {
266 for (StreamProvider streamProvider : getStreamProviders(roamingType)) {
267 try {
268 if (streamProvider.isEnabled()) {
269 streamProvider.saveContent(fileSpec, content, size, roamingType, async);
272 catch (ConnectException e) {
273 LOG.debug("Cannot send user profile to server: " + e.getLocalizedMessage());
275 catch (Exception e) {
276 LOG.debug(e);
283 public void deleteFile(final String fileSpec, final RoamingType roamingType) {
284 for (StreamProvider streamProvider : getStreamProviders(roamingType)) {
285 try {
286 if (streamProvider.isEnabled()) {
287 streamProvider.deleteFile(fileSpec, roamingType);
290 catch (Exception e) {
291 LOG.debug(e);
297 public StreamProvider[] getStreamProviders(RoamingType type) {
298 synchronized (myStreamProviders) {
299 final Collection<StreamProvider> providers = myStreamProviders.get(type);
300 return providers.toArray(new StreamProvider[providers.size()]);
304 public Collection<StreamProvider> getStreamProviders() {
305 synchronized (myStreamProviders) {
306 return Collections.unmodifiableCollection(myStreamProviders.values());
310 public InputStream loadContent(final String fileSpec, final RoamingType roamingType) throws IOException {
311 for (StreamProvider streamProvider : getStreamProviders(roamingType)) {
312 try {
313 if (streamProvider.isEnabled()) {
314 InputStream content = streamProvider.loadContent(fileSpec, roamingType);
316 if (content != null) return content;
319 catch (ConnectException e) {
320 LOG.debug("Cannot send user profile o server: " + e.getLocalizedMessage());
322 catch (Exception e) {
323 LOG.debug(e);
327 return null;
331 public String[] listSubFiles(final String fileSpec) {
332 return new String[0];
335 public boolean isEnabled() {
336 for (StreamProvider provider : getStreamProviders()) {
337 if (provider.isEnabled()) return true;
339 return false;
342 protected TrackingPathMacroSubstitutor getMacroSubstitutor(@NotNull final String fileSpec) {
343 return myPathMacroSubstitutor;
347 protected abstract XmlElementStorage.StorageData createStorageData(String storageSpec);
349 private static final Pattern MACRO_PATTERN = Pattern.compile("(\\$[^\\$]*\\$)");
351 @Nullable
352 public String expandMacroses(final String file) {
353 final Matcher matcher = MACRO_PATTERN.matcher(file);
354 while (matcher.find()) {
355 String m = matcher.group(1);
356 if (!myMacros.containsKey(m) || (!ApplicationManagerEx.getApplication().isUnitTestMode() && myMacros.get(m) == null)) {
357 throw new IllegalArgumentException("Unknown macro: " + m + " in storage spec: " + file);
362 String actualFile = file;
364 for (String macro : myMacros.keySet()) {
365 final String replacement = myMacros.get(macro);
366 /*if (replacement == null) {
367 return null;
370 if (replacement != null) {
371 actualFile = StringUtil.replace(actualFile, macro, replacement);
375 return actualFile;
378 public ExternalizationSession startExternalization() {
379 if (mySession != null) {
380 LOG.error("Starting duplicate externalization session: " + mySession);
382 ExternalizationSession session = new MyExternalizationSession();
384 mySession = session;
386 return session;
389 public SaveSession startSave(final ExternalizationSession externalizationSession) {
390 assert mySession == externalizationSession;
392 SaveSession session = createSaveSession(externalizationSession);
394 mySession = session;
396 return session;
399 protected MySaveSession createSaveSession(final ExternalizationSession externalizationSession) {
400 return new MySaveSession((MyExternalizationSession)externalizationSession);
403 public void finishSave(final SaveSession saveSession) {
404 try {
405 assert mySession == saveSession: "mySession=" + mySession + " saveSession=" + saveSession;
406 ((MySaveSession)saveSession).finishSave();
408 finally {
409 mySession = null;
410 save();
414 public void reset(){
415 mySession = null;
418 protected class MyExternalizationSession implements ExternalizationSession {
419 CompoundExternalizationSession myCompoundExternalizationSession = new CompoundExternalizationSession();
421 public void setState(@NotNull final Storage[] storageSpecs, final Object component, final String componentName, final Object state)
422 throws StateStorage.StateStorageException {
423 assert mySession == this;
425 for (Storage storageSpec : storageSpecs) {
426 StateStorage stateStorage = getStateStorage(storageSpec);
427 if (stateStorage == null) continue;
429 final StateStorage.ExternalizationSession extSession = myCompoundExternalizationSession.getExternalizationSession(stateStorage);
430 extSession.setState(component, componentName, state, storageSpec);
434 public void setStateInOldStorage(Object component, final String componentName, Object state) throws StateStorage.StateStorageException {
435 assert mySession == this;
436 StateStorage stateStorage = getOldStorage(component, componentName, StateStorageOperation.WRITE);
437 if (stateStorage != null) {
438 myCompoundExternalizationSession.getExternalizationSession(stateStorage).setState(component, componentName, state, null);
443 @Nullable
444 public StateStorage getOldStorage(Object component, final String componentName, final StateStorageOperation operation) throws StateStorage.StateStorageException {
445 return getFileStateStorage(getOldStorageSpec(component, componentName, operation));
448 protected abstract String getOldStorageSpec(Object component, final String componentName, final StateStorageOperation operation)
449 throws StateStorage.StateStorageException;
451 protected class MySaveSession implements SaveSession {
452 CompoundSaveSession myCompoundSaveSession;
455 private final String myCreationPoint;
457 @Override
458 public String toString() {
459 return super.toString() + " " + myCreationPoint;
463 public MySaveSession(final MyExternalizationSession externalizationSession) {
464 myCompoundSaveSession = new CompoundSaveSession(externalizationSession.myCompoundExternalizationSession);
467 public List<IFile> getAllStorageFilesToSave() throws StateStorage.StateStorageException {
468 assert mySession == this;
469 return myCompoundSaveSession.getAllStorageFilesToSave();
472 public List<IFile> getAllStorageFiles() {
473 return myCompoundSaveSession.getAllStorageFiles();
476 public void save() throws StateStorage.StateStorageException {
477 assert mySession == this;
479 myCompoundSaveSession.save();
482 public Set<String> getUsedMacros() {
483 assert mySession == this;
484 return myCompoundSaveSession.getUsedMacros();
487 public StateStorage.SaveSession getSaveSession(final String storage) {
488 final StateStorage stateStorage = myStorages.get(storage);
489 assert stateStorage != null;
490 return myCompoundSaveSession.getSaveSession(stateStorage);
493 public void finishSave() {
494 try {
495 LOG.assertTrue(mySession == this);
496 } finally {
497 myCompoundSaveSession.finishSave();
501 //returns set of component which were changed, null if changes are much more than just component state.
502 @Nullable
503 public Set<String> analyzeExternalChanges(final Set<Pair<VirtualFile, StateStorage>> changedFiles) {
504 Set<String> result = new HashSet<String>();
506 nextSorage: for (Pair<VirtualFile, StateStorage> pair : changedFiles) {
507 final StateStorage stateStorage = pair.second;
508 final StateStorage.SaveSession saveSession = myCompoundSaveSession.getSaveSession(stateStorage);
509 if (saveSession == null) continue nextSorage;
510 final Set<String> s = saveSession.analyzeExternalChanges(changedFiles);
512 if (s == null) return null;
513 result.addAll(s);
516 return result;
520 public void dispose() {
523 public void registerStreamProvider(StreamProvider streamProvider, final RoamingType type) {
524 synchronized (myStreamProviders) {
525 myStreamProviders.putValue(type, streamProvider);
529 public void unregisterStreamProvider(StreamProvider streamProvider, final RoamingType roamingType) {
530 synchronized (myStreamProviders) {
531 myStreamProviders.removeValue(roamingType, streamProvider);
535 public void save() {
536 String filePath = getNotNullVersionsFilePath();
537 if (filePath != null) {
538 new File(filePath).getParentFile().mkdirs();
539 try {
540 JDOMUtil.writeDocument(new Document(createComponentVersionsXml(getComponentVersions())), filePath, "\n");
542 catch (IOException e) {
543 LOG.info(e);
549 private Map<String, Long> getComponentVersions() {
550 synchronized (myComponentVersLock) {
551 if (myComponentVersions == null) {
552 myComponentVersions = loadVersions();
554 return myComponentVersions;
558 public static Element createComponentVersionsXml(Map<String, Long> versions) {
559 Element vers = new Element("versions");
561 for (String name : versions.keySet()) {
562 long version = versions.get(name);
563 if (version != 0) {
564 Element element = new Element("component");
565 vers.addContent(element);
566 element.setAttribute("name", name);
567 element.setAttribute("version", String.valueOf(version));
571 return vers;