Follow redirects in ClientGet
[jFCPlib.git] / src / main / java / net / pterodactylus / fcp / quelaton / ClientGetCommandImpl.java
1 package net.pterodactylus.fcp.quelaton;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.util.ArrayList;
6 import java.util.List;
7 import java.util.Optional;
8 import java.util.concurrent.ExecutionException;
9 import java.util.concurrent.ExecutorService;
10 import java.util.function.Consumer;
11 import java.util.function.Supplier;
12
13 import net.pterodactylus.fcp.AllData;
14 import net.pterodactylus.fcp.ClientGet;
15 import net.pterodactylus.fcp.FcpUtils.TempInputStream;
16 import net.pterodactylus.fcp.GetFailed;
17 import net.pterodactylus.fcp.Priority;
18 import net.pterodactylus.fcp.ReturnType;
19
20 import com.google.common.util.concurrent.ListeningExecutorService;
21 import com.google.common.util.concurrent.MoreExecutors;
22
23 /**
24  * Implementation of the {@link ClientGetCommand}.
25  *
26  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
27  */
28 class ClientGetCommandImpl implements ClientGetCommand {
29
30         private final ListeningExecutorService threadPool;
31         private final ConnectionSupplier connectionSupplier;
32         private final Supplier<String> identifierGenerator;
33         private final List<Consumer<String>> onRedirects = new ArrayList<>();
34
35         private boolean ignoreDataStore;
36         private boolean dataStoreOnly;
37         private Long maxSize;
38         private Priority priority;
39         private boolean realTime;
40         private boolean global;
41
42         public ClientGetCommandImpl(ExecutorService threadPool, ConnectionSupplier connectionSupplier, Supplier<String> identifierGenerator) {
43                 this.threadPool = MoreExecutors.listeningDecorator(threadPool);
44                 this.connectionSupplier = connectionSupplier;
45                 this.identifierGenerator = identifierGenerator;
46         }
47
48         @Override
49         public ClientGetCommand onRedirect(Consumer<String> onRedirect) {
50                 onRedirects.add(onRedirect);
51                 return this;
52         }
53
54         @Override
55         public ClientGetCommand ignoreDataStore() {
56                 ignoreDataStore = true;
57                 return this;
58         }
59
60         @Override
61         public ClientGetCommand dataStoreOnly() {
62                 dataStoreOnly = true;
63                 return this;
64         }
65
66         @Override
67         public ClientGetCommand maxSize(long maxSize) {
68                 this.maxSize = maxSize;
69                 return this;
70         }
71
72         @Override
73         public ClientGetCommand priority(Priority priority) {
74                 this.priority = priority;
75                 return this;
76         }
77
78         @Override
79         public ClientGetCommand realTime() {
80                 realTime = true;
81                 return this;
82         }
83
84         @Override
85         public ClientGetCommand global() {
86                 global = true;
87                 return this;
88         }
89
90         @Override
91         public Executable<Optional<Data>> uri(String uri) {
92                 return () -> threadPool.submit(() -> execute(uri));
93         }
94
95         private Optional<Data> execute(String uri) throws InterruptedException, ExecutionException, IOException {
96                 ClientGet clientGet = createClientGetCommand(identifierGenerator.get(), uri);
97                 try (ClientGetDialog clientGetDialog = new ClientGetDialog()) {
98                         return clientGetDialog.send(clientGet).get();
99                 }
100         }
101
102         private ClientGet createClientGetCommand(String identifier, String uri) {
103                 ClientGet clientGet = new ClientGet(uri, identifier, ReturnType.direct);
104                 if (ignoreDataStore) {
105                         clientGet.setIgnoreDataStore(true);
106                 }
107                 if (dataStoreOnly) {
108                         clientGet.setDataStoreOnly(true);
109                 }
110                 if (maxSize != null) {
111                         clientGet.setMaxSize(maxSize);
112                 }
113                 if (priority != null) {
114                         clientGet.setPriority(priority);
115                 }
116                 if (realTime) {
117                         clientGet.setRealTimeFlag(true);
118                 }
119                 if (global) {
120                         clientGet.setGlobal(true);
121                 }
122                 return clientGet;
123         }
124
125         private class ClientGetDialog extends FcpDialog<Optional<Data>> {
126
127                 public ClientGetDialog() throws IOException {
128                         super(ClientGetCommandImpl.this.threadPool, ClientGetCommandImpl.this.connectionSupplier.get(),
129                                 Optional.<Data>empty());
130                 }
131
132                 @Override
133                 protected void consumeAllData(AllData allData) {
134                         synchronized (this) {
135                                 String contentType = allData.getContentType();
136                                 long dataLength = allData.getDataLength();
137                                 try {
138                                         InputStream payload = new TempInputStream(allData.getPayloadInputStream(), dataLength);
139                                         setResult(Optional.of(createData(contentType, dataLength, payload)));
140                                 } catch (IOException e) {
141                                         // TODO – logging
142                                         finish();
143                                 }
144                         }
145                 }
146
147                 private Data createData(String contentType, long dataLength, InputStream payload) {
148                         return new Data() {
149                                 @Override
150                                 public String getMimeType() {
151                                         return contentType;
152                                 }
153
154                                 @Override
155                                 public long size() {
156                                         return dataLength;
157                                 }
158
159                                 @Override
160                                 public InputStream getInputStream() {
161                                         return payload;
162                                 }
163                         };
164                 }
165
166                 @Override
167                 protected void consumeGetFailed(GetFailed getFailed) {
168                         if (getFailed.getCode() == 27) {
169                                 onRedirects.forEach(onRedirect -> onRedirect.accept(getFailed.getRedirectURI()));
170                                 sendMessage(createClientGetCommand(getIdentifier(), getFailed.getRedirectURI()));
171                         } else {
172                                 finish();
173                         }
174                 }
175
176         }
177
178 }