| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.airavata.mft.transport.gcp; |
| |
| import com.google.api.gax.paging.Page; |
| import com.google.cloud.storage.Blob; |
| import com.google.cloud.storage.Bucket; |
| import com.google.cloud.storage.Storage; |
| import com.google.auth.oauth2.ServiceAccountCredentials; |
| import com.google.cloud.storage.StorageOptions; |
| |
| import org.apache.airavata.mft.agent.stub.*; |
| import org.apache.airavata.mft.core.api.MetadataCollector; |
| import org.apache.airavata.mft.credential.stubs.gcs.GCSSecret; |
| import org.apache.airavata.mft.resource.stubs.gcs.storage.GCSStorage; |
| |
| import java.io.File; |
| import java.security.PrivateKey; |
| import java.time.temporal.ChronoField; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| |
| public class GCSMetadataCollector implements MetadataCollector { |
| |
| boolean initialized = false; |
| |
| private GCSStorage gcsStorage; |
| private GCSSecret gcsSecret; |
| |
| @Override |
| public void init(StorageWrapper storage, SecretWrapper secret) { |
| this.gcsStorage = storage.getGcs(); |
| this.gcsSecret = secret.getGcs(); |
| this.initialized = true; |
| } |
| |
| private void checkInitialized() { |
| if (!initialized) { |
| throw new IllegalStateException("GCS Metadata Collector is not initialized"); |
| } |
| } |
| |
| @Override |
| public ResourceMetadata getResourceMetadata(String resourcePath, boolean recursiveSearch) throws Exception { |
| |
| checkInitialized(); |
| |
| PrivateKey privKey = GCSUtil.getPrivateKey(gcsSecret.getPrivateKey()); |
| try (Storage storage = StorageOptions.newBuilder().setCredentials(ServiceAccountCredentials.newBuilder() |
| .setProjectId(gcsSecret.getProjectId()) |
| .setPrivateKey(privKey) |
| .setClientEmail(gcsSecret.getClientEmail()) |
| .build()).build().getService()) { |
| |
| ResourceMetadata.Builder resourceBuilder = ResourceMetadata.newBuilder(); |
| |
| if (gcsStorage.getBucketName().isEmpty() && resourcePath.isEmpty()) // Load bucket list |
| { |
| Page<Bucket> buckets = storage.list(); |
| DirectoryMetadata.Builder parentDir = DirectoryMetadata.newBuilder(); |
| parentDir.setResourcePath(""); |
| parentDir.setFriendlyName(""); |
| |
| for (Bucket b : buckets.getValues()) { |
| DirectoryMetadata.Builder bucketDir = DirectoryMetadata.newBuilder(); |
| bucketDir.setFriendlyName(b.getName()); |
| bucketDir.setResourcePath(b.getName()); |
| bucketDir.setCreatedTime(b.getCreateTimeOffsetDateTime().getLong(ChronoField.INSTANT_SECONDS)); |
| bucketDir.setUpdateTime(b.getUpdateTimeOffsetDateTime().getLong(ChronoField.INSTANT_SECONDS)); |
| parentDir.addDirectories(bucketDir); |
| } |
| |
| resourceBuilder.setDirectory(parentDir); |
| return resourceBuilder.build(); |
| } |
| |
| // If directory path or top level bucket path |
| final String dirPath = resourcePath.endsWith("/") ? resourcePath : resourcePath + "/"; |
| if (resourcePath.endsWith("/") || resourcePath.isEmpty()) { // Directory |
| |
| Page<Blob> blob = storage.list(gcsStorage.getBucketName(), Storage.BlobListOption.prefix(resourcePath), Storage.BlobListOption.currentDirectory()); |
| resourceBuilder.setDirectory(processDirectory(resourcePath, blob)); |
| |
| } else { |
| |
| try { |
| Blob blob = storage.get(gcsStorage.getBucketName(), resourcePath, Storage.BlobGetOption.fields(Storage.BlobField.values())); |
| |
| if (blob != null) { |
| FileMetadata.Builder fileBuilder = FileMetadata.newBuilder(); |
| fileBuilder.setFriendlyName(blob.getName()); |
| fileBuilder.setResourcePath(resourcePath); |
| fileBuilder.setCreatedTime(blob.getCreateTimeOffsetDateTime().getLong(ChronoField.INSTANT_SECONDS)); |
| fileBuilder.setUpdateTime(blob.getUpdateTimeOffsetDateTime().getLong(ChronoField.INSTANT_SECONDS)); |
| fileBuilder.setResourceSize(blob.getSize()); |
| fileBuilder.setMd5Sum(blob.getMd5()); |
| resourceBuilder.setFile(fileBuilder); |
| |
| } else { |
| |
| Page<Blob> blobs = storage.list(gcsStorage.getBucketName(), Storage.BlobListOption.prefix(dirPath), Storage.BlobListOption.currentDirectory()); |
| resourceBuilder.setDirectory(processDirectory(resourcePath + "/", blobs)); |
| } |
| } catch (Exception e) { |
| resourceBuilder.setError(MetadataFetchError.NOT_FOUND); |
| } |
| } |
| return resourceBuilder.build(); |
| } |
| } |
| |
| @Override |
| public Boolean isAvailable(String resourcePath) throws Exception { |
| |
| checkInitialized(); |
| PrivateKey privKey = GCSUtil.getPrivateKey(gcsSecret.getPrivateKey()); |
| |
| try (Storage storage = StorageOptions.newBuilder().setCredentials(ServiceAccountCredentials.newBuilder() |
| .setProjectId(gcsSecret.getProjectId()) |
| .setPrivateKey(privKey) |
| .setClientEmail(gcsSecret.getClientEmail()) |
| .build()).build().getService()) { |
| |
| Blob blob = storage.get(gcsStorage.getBucketName(), resourcePath, Storage.BlobGetOption.fields(Storage.BlobField.values())); |
| if (blob != null) { |
| return true; |
| |
| } else { |
| |
| final String dirPath = resourcePath.endsWith("/") ? resourcePath : resourcePath + "/"; |
| try { |
| Page<Blob> blobs = storage.list(gcsStorage.getBucketName(), Storage.BlobListOption.currentDirectory(), Storage.BlobListOption.prefix(dirPath)); |
| return true; |
| } catch (Exception e) { |
| |
| return false; |
| } |
| } |
| } |
| } |
| |
| private DirectoryMetadata.Builder processDirectory(String resourcePath, Page<Blob> objectListing) { |
| Iterable<Blob> objectSummaries = objectListing.getValues(); |
| Map<String, DirectoryMetadata.Builder> subDirCache = new HashMap<>(); |
| Map<String, List<String>> childTree = new HashMap<>(); |
| childTree.put(resourcePath, new ArrayList<>()); |
| DirectoryMetadata.Builder dirBuilder = DirectoryMetadata.newBuilder(); |
| subDirCache.put(resourcePath, dirBuilder); |
| |
| for (Blob summary : objectSummaries) { |
| buildStructureRecursively(resourcePath, summary.getName(), summary, subDirCache, childTree); |
| } |
| |
| registerChildren(resourcePath, subDirCache, childTree); |
| return dirBuilder; |
| } |
| |
| private void registerChildren(String parentPath, Map<String, DirectoryMetadata.Builder> directoryStore, Map<String, List<String>> childTree) { |
| for (String childDir : childTree.get(parentPath)) { |
| registerChildren(childDir, directoryStore, childTree); |
| directoryStore.get(parentPath).addDirectories(directoryStore.get(childDir)); |
| } |
| } |
| |
| private void buildStructureRecursively(String basePath, String filePath, Blob summary, Map<String, DirectoryMetadata.Builder> directoryStore, Map<String, List<String>> childTree) { |
| |
| String relativePath = filePath.substring(basePath.length()); |
| |
| if (relativePath.contains("/")) { // A Directory |
| |
| String[] pathSections = relativePath.split("/"); |
| String thisDirKey = basePath + pathSections[0] + "/"; |
| |
| if (!directoryStore.containsKey(thisDirKey)) { |
| DirectoryMetadata.Builder subDirBuilder = DirectoryMetadata.newBuilder(); |
| subDirBuilder.setResourcePath(thisDirKey); |
| subDirBuilder.setFriendlyName(pathSections[0]); |
| directoryStore.put(thisDirKey, subDirBuilder); |
| childTree.get(basePath).add(thisDirKey); |
| childTree.put(thisDirKey, new ArrayList<>()); |
| } |
| |
| } else if (!basePath.equals(summary.getName())) { // A File |
| FileMetadata.Builder fileBuilder = FileMetadata.newBuilder(); |
| fileBuilder.setUpdateTime(summary.getUpdateTimeOffsetDateTime().getLong(ChronoField.INSTANT_SECONDS)); |
| fileBuilder.setCreatedTime(summary.getCreateTimeOffsetDateTime().getLong(ChronoField.INSTANT_SECONDS)); |
| fileBuilder.setResourcePath(summary.getName()); |
| fileBuilder.setFriendlyName(new File(summary.getName()).getName()); |
| fileBuilder.setResourceSize(summary.getSize()); |
| directoryStore.get(basePath).addFiles(fileBuilder); |
| } |
| } |
| } |