1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.tradefed.config.remote;
17 
18 import com.android.tradefed.build.BuildRetrievalError;
19 import com.android.tradefed.device.ITestDevice;
20 import com.android.tradefed.result.error.InfraErrorIdentifier;
21 
22 import java.io.File;
23 import java.util.LinkedHashMap;
24 import java.util.Map;
25 
26 import javax.annotation.Nonnull;
27 
28 /**
29  * Interface for objects that can resolve a remote file into a local one. For example:
30  * gs://bucket/dir/file.txt would be downloaded and changed to a local path.
31  */
32 public interface IRemoteFileResolver {
33 
34     /**
35      * Resolve the remote file.
36      *
37      * @param consideredFile {@link File} evaluated as remote.
38      * @return The resolved local file.
39      * @throws BuildRetrievalError if something goes wrong.
40      */
resolveRemoteFiles(File consideredFile)41     public default @Nonnull File resolveRemoteFiles(File consideredFile)
42             throws BuildRetrievalError {
43         throw new BuildRetrievalError(
44                 "Should not have been called", InfraErrorIdentifier.ARTIFACT_UNSUPPORTED_PATH);
45     }
46 
47     /**
48      * Resolve the remote file.
49      *
50      * @param consideredFile {@link File} evaluated as remote.
51      * @param queryArgs The arguments passed as a query to the URL.
52      * @return The resolved local file.
53      * @throws BuildRetrievalError if something goes wrong.
54      */
resolveRemoteFiles( File consideredFile, Map<String, String> queryArgs)55     public default @Nonnull File resolveRemoteFiles(
56             File consideredFile, Map<String, String> queryArgs) throws BuildRetrievalError {
57         return resolveRemoteFiles(consideredFile);
58     }
59 
60     /**
61      * Resolve the remote file in a future-proof interface
62      *
63      * @param args {@link RemoteFileResolverArgs} describing the remote to download and how.
64      * @return The resolved local file representation.
65      * @throws BuildRetrievalError if something goes wrong.
66      */
resolveRemoteFile(RemoteFileResolverArgs args)67     public default @Nonnull ResolvedFile resolveRemoteFile(RemoteFileResolverArgs args)
68             throws BuildRetrievalError {
69         File file = resolveRemoteFiles(args.getConsideredFile(), args.getQueryArgs());
70         return new ResolvedFile(file);
71     }
72 
73     /** Returns the associated protocol supported for download. */
getSupportedProtocol()74     public @Nonnull String getSupportedProtocol();
75 
76     /**
77      * Optional way for the implementation to receive an {@ink ITestDevice} representation of the
78      * device under tests.
79      *
80      * @param device The {@link ITestDevice} of the current invocation.
81      */
setPrimaryDevice(ITestDevice device)82     public default void setPrimaryDevice(ITestDevice device) {
83         // Do nothing by default
84     }
85 
86     /** The args passed to the resolvers */
87     public class RemoteFileResolverArgs {
88 
89         private File mConsideredFile;
90         private Map<String, String> mQueryArgs = new LinkedHashMap<>();
91         private File mDestinationDir;
92 
setConsideredFile(File consideredFile)93         public RemoteFileResolverArgs setConsideredFile(File consideredFile) {
94             mConsideredFile = consideredFile;
95             return this;
96         }
97 
addQueryArg(String key, String value)98         public RemoteFileResolverArgs addQueryArg(String key, String value) {
99             mQueryArgs.put(key, value);
100             return this;
101         }
102 
addQueryArgs(Map<String, String> queryArgs)103         public RemoteFileResolverArgs addQueryArgs(Map<String, String> queryArgs) {
104             mQueryArgs.putAll(queryArgs);
105             return this;
106         }
107 
setDestinationDir(File destinationDir)108         public RemoteFileResolverArgs setDestinationDir(File destinationDir) {
109             mDestinationDir = destinationDir;
110             return this;
111         }
112 
getConsideredFile()113         public File getConsideredFile() {
114             return mConsideredFile;
115         }
116 
getQueryArgs()117         public Map<String, String> getQueryArgs() {
118             return mQueryArgs;
119         }
120 
getDestinationDir()121         public File getDestinationDir() {
122             return mDestinationDir;
123         }
124 
125         @Override
hashCode()126         public int hashCode() {
127             final int prime = 31;
128             int result = 1;
129             result = prime * result + ((mConsideredFile == null) ? 0 : mConsideredFile.hashCode());
130             result = prime * result + ((mDestinationDir == null) ? 0 : mDestinationDir.hashCode());
131             result = prime * result + ((mQueryArgs == null) ? 0 : mQueryArgs.hashCode());
132             return result;
133         }
134 
135         @Override
equals(Object obj)136         public boolean equals(Object obj) {
137             if (this == obj) {
138                 return true;
139             }
140             if (obj == null) {
141                 return false;
142             }
143             if (getClass() != obj.getClass()) {
144                 return false;
145             }
146             RemoteFileResolverArgs other = (RemoteFileResolverArgs) obj;
147             if (mConsideredFile == null) {
148                 if (other.mConsideredFile != null) {
149                     return false;
150                 }
151             } else if (!mConsideredFile.equals(other.mConsideredFile)) {
152                 return false;
153             }
154             if (mDestinationDir == null) {
155                 if (other.mDestinationDir != null) {
156                     return false;
157                 }
158             } else if (!mDestinationDir.equals(other.mDestinationDir)) {
159                 return false;
160             }
161             if (mQueryArgs == null) {
162                 if (other.mQueryArgs != null) {
163                     return false;
164                 }
165             } else if (!mQueryArgs.equals(other.mQueryArgs)) {
166                 return false;
167             }
168             return true;
169         }
170     }
171 
172     /** Class holding information about the resolved file and some metadata. */
173     public class ResolvedFile {
174         private File mResolvedFile;
175         private boolean mShouldCleanUp = true;
176 
ResolvedFile(File resolvedFile)177         public ResolvedFile(File resolvedFile) {
178             mResolvedFile = resolvedFile;
179         }
180 
getResolvedFile()181         public File getResolvedFile() {
182             return mResolvedFile;
183         }
184 
185         /**
186          * Whether the resolved file should be deleted at the end of the invocation or not. Set to
187          * false for a file that shouldn't be deleted. For example: a local file you own.
188          */
cleanUp(boolean cleanUp)189         public ResolvedFile cleanUp(boolean cleanUp) {
190             mShouldCleanUp = cleanUp;
191             return this;
192         }
193 
shouldCleanUp()194         public boolean shouldCleanUp() {
195             return mShouldCleanUp;
196         }
197     }
198 }
199