1 /* 2 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.openjdk.skara.storage; 24 25 import org.openjdk.skara.forge.HostedRepository; 26 import org.openjdk.skara.vcs.*; 27 28 import java.io.*; 29 import java.nio.file.*; 30 import java.util.*; 31 32 class HostedRepositoryStorage<T> implements Storage<T> { 33 private final HostedRepository hostedRepository; 34 private final String ref; 35 private final String fileName; 36 private final String authorName; 37 private final String authorEmail; 38 private final String message; 39 private final Repository localRepository; 40 private final StorageSerializer<T> serializer; 41 private final StorageDeserializer<T> deserializer; 42 43 private Hash hash; 44 private RepositoryStorage<T> repositoryStorage; 45 private Set<T> current; 46 47 HostedRepositoryStorage(HostedRepository repository, Path localStorage, String ref, String fileName, String authorName, String authorEmail, String message, StorageSerializer<T> serializer, StorageDeserializer<T> deserializer) { 48 this.hostedRepository = repository; 49 this.ref = ref; 50 this.fileName = fileName; 51 this.authorEmail = authorEmail; 52 this.authorName = authorName; 53 this.message = message; 54 this.serializer = serializer; 55 this.deserializer = deserializer; 56 57 try { 58 Repository localRepository; 59 try { 60 localRepository = Repository.materialize(localStorage, repository.url(), "+" + ref + ":storage"); 61 } catch (IOException e) { 62 // The remote ref may not yet exist 63 localRepository = Repository.init(localStorage, repository.repositoryType()); 64 var storage = Files.writeString(localStorage.resolve(fileName), ""); 65 localRepository.add(storage); 66 localRepository.commit(message, authorName, authorEmail); 67 } 68 this.localRepository = localRepository; 69 hash = localRepository.head(); 70 repositoryStorage = new RepositoryStorage<>(localRepository, fileName, authorName, authorEmail, message, serializer, deserializer); 71 current = current(); 72 } catch (IOException e) { 73 throw new UncheckedIOException(e); 74 } 75 } 76 77 @Override 78 public Set<T> current() { 79 return repositoryStorage.current(); 80 } 81 82 @Override 83 public void put(Collection<T> items) { 84 int retryCount = 0; 85 IOException lastException = null; 86 Hash lastRemoteHash = null; 87 88 while (retryCount < 10) { 89 // Update our local storage 90 repositoryStorage.put(items); 91 var updated = repositoryStorage.current(); 92 if (current.equals(updated)) { 93 return; 94 } 95 96 // The local storage has changed, try to push it to the remote 97 try { 98 var updatedHash = localRepository.head(); 99 localRepository.push(updatedHash, hostedRepository.url(), ref); 100 hash = updatedHash; 101 current = updated; 102 return; 103 } catch (IOException e) { 104 lastException = e; 105 106 // Check if the remote has changed 107 try { 108 var remoteHash = localRepository.fetch(hostedRepository.url(), ref); 109 if (!remoteHash.equals(lastRemoteHash)) { 110 localRepository.checkout(remoteHash, true); 111 repositoryStorage = new RepositoryStorage<>(localRepository, fileName, authorName, authorEmail, message, serializer, deserializer); 112 lastRemoteHash = remoteHash; 113 114 // We are making progress catching up with remote changes, don't update the retryCount 115 continue; 116 } 117 } catch (IOException e1) { 118 lastException = e1; 119 } 120 retryCount++; 121 } 122 } 123 124 throw new UncheckedIOException("Retry count exceeded", lastException); 125 } 126 }