◼︎ 환경
- Model : MacBook Pro (14-inch, 2021)
- CPU : Apple M1 Pro
- MENORY : 16GB
- DISK : 512 GB SSD
- OS : macOS 13.2.4 (22F66)
- TOOLS : Visual Studio Code, Java 11, Gradle, Docker
- Version Control : GitHub
- Programming Language : Java
- Framework : Spring Boot 2.7.12, Spring Security 5.7.7
- Cache : ehcahce 2.10.9.2, Guava 31.1-jre
- DBMS : MySql 8.0.33
- Cloud : OCI (free tier account)
OCI 오브젝트 스토이지 버킷 생성
OCI 오브젝트 스토리지 버킷 생성은 다음 절차로 진행한다. ( 계정은 free tier 을 이용하였다. )※ 스토이지 버킷(Storage Bucket)는 대부분의 클라우드 기반 객체 스토리지 서비스에서 사용되는 컨테이너 개념으로 Amazon S3, Google Cloud Storage, Microsoft Azure Blob Storage, Oracle Cloud Object Storage 등에서 사용된다. 파일이나 데이터를 버킷에 저장하면 클라우드 제공 업체가 제공하는 다양한 기능(데이터의 보관, 버전 관리, 접근 제어, 보안, 데이터 복제)을 활용할 수 있다.
- OCI 콘솔 왼쪽 상단의 햄버거 아이콘을 클릭
- 스토이지 를 클릭합
- 오브젝트 스토리지 및 아카이브 스토리지 아래에 있는 "버킷"을 클릭
- 버킷 생성 버튼을 클릭. 팝업 창이 열리면 "생성"을 클릭(기본 구성 유지)
API 연계를 위한 개인키 구성 파일 생성
프로그램에서 OCI 오브젝트 스토리지와 연결하려면 개인 키와 구성 파일을 생성해야 한다. 생성은 아래 절차로 진행한다.
- OCI 콘솔에서 프로필 세부 정보 페이지로 이동
- API 키 탭으로 이동
- API 키 추가 버튼을 클릭
- 프라이빗 키 다운로드 버튼을 클릭 파일을 다운로드
- 다운로드한 비밀키 파일을 스프링 부팅 애플리케이션 루트에 복사
- 추가 버튼을 를 클릭 API 키를 추가
- 구성 파일 미리보기 복사 링크를 클릭하여 구성파일 내용을 복사
- 스프링 부팅 애플리케이션 프로젝트 루트 폴더에 파일 확장자 없이 "config"라는 이름의 파일을 만들고 복사한 콘텐츠를 추가. config 파일에서 다운로드한 개인 키가 저장된 경로를 업데이트 (<path to your private keyfile>)
스프링 부트 의존성 추가
스프링 부트 애플리케이션 프로젝트에 OCI Java SDK 사용을 위한 의존성을 build.gradle 파일에 추가한다. 추가되는 의존성은 아래와 같다.- implementation 'com.oracle.oci.sdk:oci-java-sdk:3.36.0'
- implementation 'com.oracle.oci.sdk:oci-java-sdk-objectstorage:3.36.0'
- implementation 'com.oracle.oci.sdk:oci-java-sdk-common-httpclient-jersey:3.36.0'
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
apply from: '../../version.gradle' | |
description = "oracle-objectstorage" | |
version = loadVersion().getVersion() | |
bootJar { enabled = false } | |
println " build ${description} ${version} using spring boot ${springBootVersion} (${buildApplicationVersion})" | |
jar { | |
enabled = true | |
manifest { | |
attributes('Implementation-Title': 'Architecture Modules OCI ObjectStorage', 'Implementation-Version': version ) | |
} | |
} | |
dependencies { | |
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") | |
testImplementation("org.junit.jupiter:junit-jupiter:${junitJupiterVersion}") | |
implementation 'com.oracle.oci.sdk:oci-java-sdk:3.36.0' | |
implementation 'com.oracle.oci.sdk:oci-java-sdk-objectstorage:3.36.0' | |
implementation 'com.oracle.oci.sdk:oci-java-sdk-common-httpclient-jersey:3.36.0' | |
implementation project(':architecture-web') | |
} |
OCI 오브젝트 스토리지 클라이언트 클래스 구현
다음으로 앞서 생성한 OCI 오브젝트 스토리지 접속을 위한 클라이언트 클래스를 구현한다.
SDK 에서 제공하는 API 는 앞서 저장한 OCI config file 과 비밀키를 읽어드리기 위하여 파일위치를 지정하도록 되어 있다. 배포의 용의성을 위해서 프로젝트 리소스에서 읽어드릴 수 있도록 SDK 소스를 일부를 아래와 같이 수정하여 사용하였다. (src/main/resources/oci 경로에 config 와 privite_key.pem 파일 복사, config 파일 key_file 값을 oci/private_key.pem 수정 )
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package architecture.studio.services.spring.config.oci; | |
import java.io.IOException; | |
import org.springframework.core.io.Resource; | |
import org.springframework.core.io.ResourceLoader; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.Configuration; | |
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.beans.factory.annotation.Qualifier; | |
import org.springframework.beans.factory.annotation.Value; | |
import com.oracle.bmc.ConfigFileReader; | |
import com.oracle.bmc.objectstorage.ObjectStorage; | |
import com.oracle.bmc.objectstorage.ObjectStorageClient; | |
import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider; | |
import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider; | |
import lombok.extern.slf4j.Slf4j; | |
import lombok.RequiredArgsConstructor; | |
@Slf4j | |
@RequiredArgsConstructor | |
@Configuration | |
public class OrgOciClientConfig { | |
public static final String SERVICE_NAME = "components:objectstorage:oci"; | |
@Autowired | |
private ResourceLoader resourceLoader; | |
// Path to OCI config file | |
@Value("${studio.modules.oci.config.path:oci/config}") | |
String configurationFilePath; | |
@Value("${studio.modules.oci.config.profile:DEFAULT}") | |
String profile ; | |
@ConditionalOnProperty( value= "studio.modules.oci.objectstorage.enabled", havingValue = "true" , matchIfMissing= true ) | |
@Bean(SERVICE_NAME) | |
public ObjectStorage objectStorage() throws IOException { | |
return ObjectStorageClient.builder().build(getAuthenticationProvider()); | |
} | |
private AbstractAuthenticationDetailsProvider getAuthenticationProvider() throws IOException { | |
Resource resource = resourceLoader.getResource(configurationFilePath); | |
log.debug("Finding OCI configuration file <{}> {}", resource, resource.exists()); | |
final ConfigFileReader.ConfigFile configFile = ConfigFileReader.parse(resource.getInputStream(), profile); | |
return new ConfigFileAuthenticationDetailsProvider(configFile); | |
} | |
} |
- ConfigFileAuthenticationDetailsProvider -> ClasspathConfigFileAuthenticationDetailsProvider.java
- SimplePrivateKeySupplier.java -> ClasspathPrivateKeySupplier.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package architecture.studio.services.spring.config.oci; | |
import com.oracle.bmc.internal.Alloy; | |
import com.oracle.bmc.ConfigFileReader; | |
import com.oracle.bmc.ConfigFileReader.ConfigFile; | |
import com.oracle.bmc.Realm; | |
import com.oracle.bmc.Region; | |
import com.oracle.bmc.auth.internal.ConfigFileDelegationTokenUtils; | |
import com.oracle.bmc.auth.internal.DelegationTokenConfigurator; | |
import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider; | |
import com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider; | |
import com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider; | |
import com.oracle.bmc.auth.AuthenticationDetailsProvider; | |
import com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider; | |
import com.oracle.bmc.auth.BasicConfigFileAuthenticationProvider; | |
import com.oracle.bmc.auth.RegionProvider; | |
import com.oracle.bmc.auth.SimplePrivateKeySupplier; | |
import com.oracle.bmc.auth.ProvidesClientConfigurators; | |
import com.oracle.bmc.http.ClientConfigurator; | |
import com.oracle.bmc.util.internal.StringUtils; | |
import com.oracle.bmc.util.internal.Validate; | |
import org.slf4j.Logger; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.function.Supplier; | |
/** | |
* Implementation of {@link AuthenticationDetailsProvider} that uses a standard OCI configuration | |
* file as an input. | |
*/ | |
public class ClasspathConfigFileAuthenticationDetailsProvider | |
implements AuthenticationDetailsProvider, RegionProvider, ProvidesClientConfigurators { | |
private static final String OCI_REGION_ENV_VAR_NAME = "OCI_REGION"; | |
private static final String AUTHENTICATION_TYPE_KEY = "authentication_type"; | |
private static final String INSTANCE_PRINCIPAL_AUTHENTICATION_TYPE_VALUE = "instance_principal"; | |
private static final String RESOURCE_PRINCIPAL_AUTHENTICATION_TYPE_VALUE = "resource_principal"; | |
private static final Logger LOG = | |
org.slf4j.LoggerFactory.getLogger(ConfigFileAuthenticationDetailsProvider.class); | |
protected final BasicConfigFileAuthenticationProvider delegate; | |
private final Region region; | |
private static final String CONFIG_FILE_DEBUG_INFORMATION_LOG = | |
"\nFor more information about OCI configuration file and how to get required information, see https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdkconfig.htm"; | |
/** | |
* Creates a new instance using the config file at the default location, see {@link | |
* ConfigFileReader#DEFAULT_FILE_PATH}. | |
* | |
* @param profile profile to load, optional | |
* @throws IOException if the configuration file could not be loaded | |
*/ | |
public ClasspathConfigFileAuthenticationDetailsProvider(String profile) throws IOException { | |
this(ConfigFileReader.parseDefault(profile)); | |
} | |
/** | |
* Creates a new instance. | |
* | |
* @param configurationFilePath path to the OCI configuration file | |
* @param profile profile to load, optional | |
* @throws IOException if the configuration file could not be loaded | |
*/ | |
public ClasspathConfigFileAuthenticationDetailsProvider(String configurationFilePath, String profile) | |
throws IOException { | |
this(ConfigFileReader.parse(configurationFilePath, profile)); | |
} | |
/** | |
* Creates a new instance. | |
* | |
* @param configFile The configuration file to use. | |
*/ | |
public ClasspathConfigFileAuthenticationDetailsProvider(ConfigFile configFile) { | |
final String authentication_type = configFile.get(AUTHENTICATION_TYPE_KEY); | |
if (INSTANCE_PRINCIPAL_AUTHENTICATION_TYPE_VALUE.equals(authentication_type)) { | |
this.delegate = | |
new ConfigFileInstancePrincipalAuthenticationDetailsProvider(configFile); | |
} else if (RESOURCE_PRINCIPAL_AUTHENTICATION_TYPE_VALUE.equals(authentication_type)) { | |
LOG.debug( | |
"Authentication type in config file: " | |
+ configFile.get(AUTHENTICATION_TYPE_KEY)); | |
this.delegate = | |
new ConfigFileResourcePrincipalAuthenticationDetailsProvider(configFile); | |
} else { | |
this.delegate = new ConfigFileSimpleAuthenticationDetailsProvider(configFile); | |
} | |
this.region = getRegionFromConfigFile(configFile); | |
} | |
public static Region getRegionFromConfigFile(ConfigFile configFile) { | |
// region is optional, for backwards compatibility, if region is not known, log an error and | |
// continue. | |
// the same file may be used by other tools, where the region can be a newly launched region | |
// value | |
// that is not supported by the SDK yet. | |
Region region = null; | |
String regionId = configFile.get("region"); | |
// if regionId is not defined in config file check env variable | |
if (StringUtils.isBlank(regionId)) { | |
regionId = System.getenv(OCI_REGION_ENV_VAR_NAME); | |
LOG.info("regionId from OCI_REGION env variable: " + regionId); | |
} | |
if (regionId != null) { | |
try { | |
region = Region.fromRegionId(regionId); | |
} catch (IllegalArgumentException e) { | |
Alloy.throwUnknownAlloyRegionIfAppropriate(regionId, e); | |
LOG.warn( | |
"Found regionId '{}' in config file or OCI_REGION env variable, but not supported by this version of the SDK." | |
+ CONFIG_FILE_DEBUG_INFORMATION_LOG, | |
regionId, | |
e); | |
// Proceed by assuming the region id in the config file belongs to OC1 realm. | |
region = Region.register(regionId, Realm.OC1); | |
} | |
} else { | |
LOG.info( | |
"Region not specified in Config file or OCI_REGION env variable. Proceeding without setting a region."); | |
} | |
return region; | |
} | |
@Override | |
public String getFingerprint() { | |
return this.delegate.getFingerprint(); | |
} | |
@Override | |
public String getTenantId() { | |
return this.delegate.getTenantId(); | |
} | |
@Override | |
public String getUserId() { | |
return this.delegate.getUserId(); | |
} | |
@Override | |
public List<ClientConfigurator> getClientConfigurators() { | |
return this.delegate.getClientConfigurators(); | |
} | |
@Deprecated | |
@Override | |
public String getPassPhrase() { | |
return this.delegate.getPassPhrase(); | |
} | |
@Override | |
public char[] getPassphraseCharacters() { | |
return this.delegate.getPassphraseCharacters(); | |
} | |
@Override | |
public InputStream getPrivateKey() { | |
return this.delegate.getPrivateKey(); | |
} | |
@Override | |
public String getKeyId() { | |
return this.delegate.getKeyId(); | |
} | |
@Override | |
public Region getRegion() { | |
return this.region; | |
} | |
/** | |
* Returns the file path to the private key. | |
* | |
* @return the PEM File Path. | |
*/ | |
public String getPemFilePath() { | |
return this.delegate.getPemFilePath(); | |
} | |
public String toString() { | |
return "ConfigFileAuthenticationDetailsProvider(delegate=" | |
+ this.delegate | |
+ ", region=" | |
+ this.getRegion() | |
+ ")"; | |
} | |
protected static class ConfigFileSimpleAuthenticationDetailsProvider | |
implements BasicConfigFileAuthenticationProvider { | |
private final SimpleAuthenticationDetailsProvider delegate; | |
private final String pemFilePath; | |
private final List<ClientConfigurator> clientConfigurators; | |
protected ConfigFileSimpleAuthenticationDetailsProvider(ConfigFile configFile) { | |
String fingerprint = | |
Validate.notNull( | |
configFile.get("fingerprint"), | |
"Missing fingerprint in config." + CONFIG_FILE_DEBUG_INFORMATION_LOG); | |
String tenantId = | |
Validate.notNull( | |
configFile.get("tenancy"), | |
"Missing tenancy in config." + CONFIG_FILE_DEBUG_INFORMATION_LOG); | |
String userId = | |
Validate.notNull( | |
configFile.get("user"), | |
"Missing user in config." + CONFIG_FILE_DEBUG_INFORMATION_LOG); | |
String pemFilePath = | |
Validate.notNull( | |
configFile.get("key_file"), | |
"Missing key_file in config." + CONFIG_FILE_DEBUG_INFORMATION_LOG); | |
// pass phrase is optional | |
String passPhrase = configFile.get("pass_phrase"); | |
Supplier<InputStream> privateKeySupplier = new ClasspathPrivateKeySupplier(pemFilePath); | |
SimpleAuthenticationDetailsProvider.SimpleAuthenticationDetailsProviderBuilder builder = | |
SimpleAuthenticationDetailsProvider.builder() | |
.privateKeySupplier(privateKeySupplier) | |
.fingerprint(fingerprint) | |
.userId(userId) | |
.tenantId(tenantId); | |
if (passPhrase != null) { | |
builder = builder.passphraseCharacters(passPhrase.toCharArray()); | |
} | |
this.delegate = builder.build(); | |
this.pemFilePath = pemFilePath; | |
this.clientConfigurators = new ArrayList<>(); | |
} | |
public String getFingerprint() { | |
return this.delegate.getFingerprint(); | |
} | |
public String getTenantId() { | |
return this.delegate.getTenantId(); | |
} | |
public String getUserId() { | |
return this.delegate.getUserId(); | |
} | |
@Deprecated | |
public String getPassPhrase() { | |
return this.delegate.getPassPhrase(); | |
} | |
@Deprecated | |
public char[] getPassphraseCharacters() { | |
return this.delegate.getPassphraseCharacters(); | |
} | |
public InputStream getPrivateKey() { | |
return this.delegate.getPrivateKey(); | |
} | |
public String getKeyId() { | |
return this.delegate.getKeyId(); | |
} | |
@Override | |
public String getPemFilePath() { | |
return this.pemFilePath; | |
} | |
@Override | |
public List<ClientConfigurator> getClientConfigurators() { | |
return this.clientConfigurators; | |
} | |
} | |
protected static class ConfigFileInstancePrincipalAuthenticationDetailsProvider | |
implements BasicConfigFileAuthenticationProvider { | |
private final InstancePrincipalsAuthenticationDetailsProvider delegate; | |
private final String tenantId; | |
private final List<ClientConfigurator> clientConfigurators; | |
protected ConfigFileInstancePrincipalAuthenticationDetailsProvider(ConfigFile configFile) { | |
this.delegate = InstancePrincipalsAuthenticationDetailsProvider.builder().build(); | |
String tenantId = configFile.get("tenancy"); | |
if (tenantId == null) tenantId = ""; | |
this.tenantId = tenantId; | |
this.clientConfigurators = new ArrayList<>(); | |
try { | |
String delegationToken = | |
ConfigFileDelegationTokenUtils.parseAndGetToken(configFile); | |
if (!StringUtils.isBlank(delegationToken)) { | |
this.clientConfigurators.add(new DelegationTokenConfigurator(delegationToken)); | |
} | |
} catch (Exception e) { | |
LOG.debug("Could not load delegation token!", e); | |
} | |
} | |
@Override | |
public String getFingerprint() { | |
return null; | |
} | |
@Override | |
public String getTenantId() { | |
return this.tenantId; | |
} | |
@Override | |
public String getUserId() { | |
return null; | |
} | |
@Override | |
public String getKeyId() { | |
return this.delegate.getKeyId(); | |
} | |
@Override | |
public InputStream getPrivateKey() { | |
return this.delegate.getPrivateKey(); | |
} | |
@Override | |
public String getPassPhrase() { | |
return null; | |
} | |
@Override | |
public char[] getPassphraseCharacters() { | |
return null; | |
} | |
@Override | |
public String getPemFilePath() { | |
return null; | |
} | |
@Override | |
public List<ClientConfigurator> getClientConfigurators() { | |
return this.clientConfigurators; | |
} | |
} | |
protected static class ConfigFileResourcePrincipalAuthenticationDetailsProvider | |
implements BasicConfigFileAuthenticationProvider { | |
private final ResourcePrincipalAuthenticationDetailsProvider delegate; | |
private final String tenantId; | |
private final List<ClientConfigurator> clientConfigurators; | |
protected ConfigFileResourcePrincipalAuthenticationDetailsProvider(ConfigFile configFile) { | |
this.delegate = ResourcePrincipalAuthenticationDetailsProvider.builder().build(); | |
String tenantId = configFile.get("tenancy"); | |
if (tenantId == null) { | |
tenantId = ""; | |
} | |
this.tenantId = tenantId; | |
this.clientConfigurators = new ArrayList<>(); | |
try { | |
String delegationToken = | |
ConfigFileDelegationTokenUtils.parseAndGetToken(configFile); | |
if (!StringUtils.isBlank(delegationToken)) { | |
this.clientConfigurators.add(new DelegationTokenConfigurator(delegationToken)); | |
} | |
} catch (Exception e) { | |
LOG.debug("Could not load delegation token!", e); | |
} | |
} | |
@Override | |
public String getFingerprint() { | |
return null; | |
} | |
@Override | |
public String getTenantId() { | |
return this.tenantId; | |
} | |
@Override | |
public String getUserId() { | |
return null; | |
} | |
@Override | |
public String getKeyId() { | |
return this.delegate.getKeyId(); | |
} | |
@Override | |
public InputStream getPrivateKey() { | |
return this.delegate.getPrivateKey(); | |
} | |
@Override | |
public String getPassPhrase() { | |
return null; | |
} | |
@Override | |
public char[] getPassphraseCharacters() { | |
return null; | |
} | |
@Override | |
public String getPemFilePath() { | |
return null; | |
} | |
@Override | |
public List<ClientConfigurator> getClientConfigurators() { | |
return this.clientConfigurators; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package architecture.studio.services.spring.config.oci; | |
import org.springframework.core.io.Resource; | |
import org.springframework.core.io.ClassPathResource; | |
import java.io.FileNotFoundException; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.util.function.Supplier; | |
/** Simple Supplier for the private key that handles missing files. */ | |
public class ClasspathPrivateKeySupplier implements Supplier<InputStream> { | |
private final String pemFilePath; | |
@java.beans.ConstructorProperties({"pemFilePath"}) | |
public ClasspathPrivateKeySupplier(String pemFilePath) { | |
this.pemFilePath = pemFilePath; | |
} | |
@Override | |
public InputStream get() { | |
try { | |
Resource resource = new ClassPathResource(pemFilePath); | |
return resource.getInputStream(); //new FileInputStream(new File(expandUserHome(pemFilePath))); | |
} catch (IOException e) { | |
throw new IllegalArgumentException("Could not find private key: " + pemFilePath, e); | |
} | |
} | |
public String toString() { | |
return "ClasspathPrivateKeySupplier(pemFilePath=" + this.pemFilePath + ")"; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package architecture.studio.services.spring.config.oci; | |
import java.io.IOException; | |
import org.springframework.core.io.ClassPathResource; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.Configuration; | |
import org.springframework.context.annotation.Lazy; | |
import org.springframework.beans.factory.annotation.Value; | |
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
import com.oracle.bmc.ConfigFileReader; | |
import com.oracle.bmc.objectstorage.ObjectStorage; | |
import com.oracle.bmc.objectstorage.ObjectStorageClient; | |
import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider; | |
import lombok.extern.slf4j.Slf4j; | |
import lombok.RequiredArgsConstructor; | |
@Slf4j | |
@RequiredArgsConstructor | |
@Configuration | |
public class OciClientConfig { | |
public static final String SERVICE_NAME = "components:objectstorage:oci"; | |
// Path to OCI config file | |
@Value("${studio.modules.oci.config.path:oci/config}") | |
String configurationFilePath; | |
@Value("${studio.modules.oci.config.profile:DEFAULT}") | |
String profile ; | |
@ConditionalOnProperty( value= "studio.modules.oci.objectstorage.enabled", havingValue = "true" , matchIfMissing= true ) | |
@Bean(SERVICE_NAME) | |
public ObjectStorage objectStorage() throws IOException { | |
return ObjectStorageClient.builder().build(getAuthenticationProvider()); | |
} | |
private AbstractAuthenticationDetailsProvider getAuthenticationProvider() throws IOException { | |
Resource resource = new ClassPathResource(configurationFilePath); | |
final ConfigFileReader.ConfigFile configFile = ConfigFileReader.parse(resource.getInputStream(), profile); | |
return new ClasspathConfigFileAuthenticationDetailsProvider(configFile); | |
} | |
} |
OCI 오브젝트 스토리지 테스트 클래스 구현
OCI 오브젝트 스토이지에 파일을 추가하려면 먼저 버킷이 필요하다. 생성된 버킷 목록을 조회하려면 compartmentId 와 namespaceName 값이 요구된다.
⑴ namespaceName 값은 우측 상단 사용자 아이콘을 클릭하고, 테넌시 이름을 클릭 테넌시 상세 페이지로 이동한다. 테넌시 정보 구획에서 오브젝트 스토이지 네임스페이스 값이 namespaceName 이다.
⑵ compartmentId 는 OCI 콘솔 > ID& 보안 > 구획 메뉴를 통하여 확인 할 수 있다. 목록에서 이름을 클릭, "구획정보"섹션에서 "OCID"를 값을 확인할 수 있는데 이 값이 해당 구획(compartment) 의 compartmentId 값이다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package architecture.studio.services; | |
import java.util.ArrayList; | |
import java.util.List; | |
import org.junit.jupiter.api.Test; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.beans.factory.annotation.Qualifier; | |
import org.springframework.boot.test.context.SpringBootTest; | |
import com.oracle.bmc.objectstorage.ObjectStorage; | |
import com.oracle.bmc.objectstorage.model.BucketSummary; | |
import com.oracle.bmc.objectstorage.requests.ListBucketsRequest; | |
import com.oracle.bmc.objectstorage.responses.ListBucketsResponse; | |
import architecture.studio.components.objectstorage.spring.config.OciClientConfig; | |
import lombok.extern.slf4j.Slf4j; | |
@Slf4j | |
@SpringBootTest(classes=architecture.studio.components.objectstorage.spring.config.OciClientConfig.class) | |
public class TestOciBucketList { | |
@Autowired | |
@Qualifier(OciClientConfig.SERVICE_NAME) | |
ObjectStorage client; | |
@Test | |
public void test() throws Exception { | |
// 버킷 목록 조회 요청 생성 | |
// OCI 계정의 compartmentId 입력 | |
ListBucketsRequest listBucketsRequest = ListBucketsRequest.builder() | |
.namespaceName("<namespace name>") | |
.compartmentId("<compartment Id>") | |
.build(); | |
// 버킷 목록 조회 | |
List<BucketSummary> bucketList = new ArrayList<BucketSummary>(); | |
try { | |
ListBucketsResponse response = client.listBuckets(listBucketsRequest); | |
bucketList = response.getItems(); | |
System.out.println( "" + response); | |
} catch (Exception e) { | |
System.out.println("Error getting bucket list: " + e.getMessage()); | |
} | |
// 조회된 버킷 목록 출력 | |
System.out.println("Bucket List:"); | |
for (BucketSummary bucket : bucketList) { | |
System.out.println(bucket.getName() + ", " + bucket.getCompartmentId() + ", " + bucket.getNamespace()); | |
} | |
} | |
} |
OCI 오브젝트 스토이지에 파일을 추가하는 것은 PutObjectRequest 을 생성하여 쉽게 구현할 수 있다. 주의할 점은 object name 은 버킷 내에서 고유 해야 한다는 점이다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package architecture.studio.services; | |
import java.io.File; | |
import java.io.FileInputStream; | |
import org.apache.commons.codec.binary.StringUtils; | |
import org.apache.commons.io.FileUtils; | |
import org.junit.jupiter.api.Test; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.beans.factory.annotation.Qualifier; | |
import org.springframework.boot.test.context.SpringBootTest; | |
import com.oracle.bmc.model.BmcException; | |
import com.oracle.bmc.objectstorage.ObjectStorage; | |
import com.oracle.bmc.objectstorage.requests.GetObjectRequest; | |
import com.oracle.bmc.objectstorage.requests.PutObjectRequest; | |
import com.oracle.bmc.objectstorage.responses.GetObjectResponse; | |
import com.oracle.bmc.objectstorage.responses.PutObjectResponse; | |
import architecture.studio.services.spring.config.oci.OciClientConfig; | |
import lombok.extern.slf4j.Slf4j; | |
@Slf4j | |
@SpringBootTest(classes = architecture.studio.services.spring.config.oci.OciClientConfig.class) | |
public class TestOciObjectUpload { | |
@Autowired | |
@Qualifier(OciClientConfig.SERVICE_NAME) | |
ObjectStorage client; | |
@Test | |
public void test() throws Exception { | |
/* | |
* you can find namespace in your bucket details page under | |
* Bucket information in OCI console | |
*/ | |
String namespaceName = "<namespace name>"; | |
// 버킷 이름 설정 | |
String bucketName = "<bucket name>"; | |
String objectName = "11111222223333444"; // must be unique | |
File file = new File("file to upload."); | |
long filesize = FileUtils.sizeOf(file); | |
FileInputStream inputStream = new FileInputStream(file); | |
PutObjectRequest putObjectRequest = PutObjectRequest.builder() | |
.namespaceName(namespaceName) | |
.bucketName(bucketName) | |
.objectName(objectName) | |
.contentLength(filesize) | |
.putObjectBody(inputStream) | |
.build(); | |
if (isExist(namespaceName, bucketName, objectName)) | |
return; | |
PutObjectResponse response = client.putObject(putObjectRequest); | |
log.debug("Upload : {}", response); | |
log.debug("Object : {}", getObject(namespaceName, bucketName, objectName)); | |
} | |
public boolean isExist(String namespaceName, String bucketName, String objectName) { | |
try { | |
getObject(namespaceName, bucketName, objectName); | |
} catch (BmcException e) { | |
if (StringUtils.equals(e.getServiceCode(), "ObjectNotFound")) | |
return false; | |
} | |
return true; | |
} | |
public GetObjectResponse getObject(String namespaceName, String bucketName, String objectName) { | |
GetObjectRequest getObjectRequest = GetObjectRequest.builder() | |
.namespaceName(namespaceName) | |
.bucketName(bucketName) | |
.objectName(objectName).build(); | |
return client.getObject(getObjectRequest); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package architecture.studio.services; | |
import java.util.List; | |
import org.junit.jupiter.api.Test; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.beans.factory.annotation.Qualifier; | |
import org.springframework.boot.test.context.SpringBootTest; | |
import com.oracle.bmc.objectstorage.ObjectStorage; | |
import com.oracle.bmc.objectstorage.model.ObjectSummary; | |
import com.oracle.bmc.objectstorage.requests.GetObjectRequest; | |
import com.oracle.bmc.objectstorage.requests.ListObjectsRequest; | |
import com.oracle.bmc.objectstorage.responses.GetObjectResponse; | |
import com.oracle.bmc.objectstorage.responses.ListObjectsResponse; | |
import architecture.studio.services.spring.config.oci.OciClientConfig; | |
import lombok.extern.slf4j.Slf4j; | |
@Slf4j | |
@SpringBootTest(classes=architecture.studio.services.spring.config.oci.OciClientConfig.class) | |
public class TestOciClient { | |
@Autowired | |
@Qualifier(OciClientConfig.SERVICE_NAME) | |
ObjectStorage client; | |
@Test | |
public void test() throws Exception { | |
// OCI Object Storage client check. | |
log.info("oci : {}" , client ); | |
/* you can find namespace in your bucket details page under | |
Bucket information in OCI console */ | |
String namespaceName = "<namespace name>"; | |
// 버킷 이름 설정 | |
String bucketName = "<bucket name>"; | |
// 객체 목록 조회 | |
ListObjectsRequest listObjectsRequest = ListObjectsRequest.builder() | |
.namespaceName(namespaceName) | |
.bucketName(bucketName) | |
.build(); | |
ListObjectsResponse listObjectsResponse = client.listObjects(listObjectsRequest); | |
List<ObjectSummary> objectSummaries = listObjectsResponse.getListObjects().getObjects(); | |
// 조회된 객체 목록 출력 | |
for (ObjectSummary objectSummary : objectSummaries) { | |
log.debug("Object: {}, {}", objectSummary.getName(), objectSummary); | |
GetObjectResponse objectDetails = getObject(namespaceName, bucketName, objectSummary.getName()); | |
log.debug("Object : {}", objectDetails ); | |
} | |
} | |
public GetObjectResponse getObject(String namespaceName, String bucketName, String objectName){ | |
GetObjectRequest getObjectRequest = GetObjectRequest.builder() | |
.namespaceName(namespaceName) | |
.bucketName(bucketName) | |
.objectName(objectName).build(); | |
return client.getObject(getObjectRequest); | |
} | |
} |
파일 목록을 조회하고 객체에 대한 사전 인증된 요청 URL 을 생성하는 테스트 코드를 만들어 본다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package architecture.studio.services; | |
import java.time.Instant; | |
import java.util.Date; | |
import java.util.List; | |
import java.util.UUID; | |
import org.junit.jupiter.api.Test; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.beans.factory.annotation.Qualifier; | |
import org.springframework.boot.test.context.SpringBootTest; | |
import com.oracle.bmc.objectstorage.ObjectStorage; | |
import com.oracle.bmc.objectstorage.model.CreatePreauthenticatedRequestDetails; | |
import com.oracle.bmc.objectstorage.model.ObjectSummary; | |
import com.oracle.bmc.objectstorage.model.PreauthenticatedRequest; | |
import com.oracle.bmc.objectstorage.requests.CreatePreauthenticatedRequestRequest; | |
import com.oracle.bmc.objectstorage.requests.ListObjectsRequest; | |
import com.oracle.bmc.objectstorage.responses.CreatePreauthenticatedRequestResponse; | |
import com.oracle.bmc.objectstorage.responses.ListObjectsResponse; | |
import architecture.studio.services.spring.config.oci.OciClientConfig; | |
import lombok.extern.slf4j.Slf4j; | |
@Slf4j | |
@SpringBootTest(classes=architecture.studio.services.spring.config.oci.OciClientConfig.class) | |
public class TestOciObjectUrl { | |
@Autowired | |
@Qualifier(OciClientConfig.SERVICE_NAME) | |
ObjectStorage client; | |
@Test | |
public void test() throws Exception { | |
/* you can find namespace in your bucket details page under | |
Bucket information in OCI console */ | |
String namespaceName = "<namespace name>"; | |
// 버킷 이름 설정 | |
String bucketName = "<bucket name>"; | |
// 객체 목록 조회 | |
ListObjectsRequest listObjectsRequest = ListObjectsRequest.builder() | |
.namespaceName(namespaceName) | |
.bucketName(bucketName) | |
.build(); | |
ListObjectsResponse listObjectsResponse = client.listObjects(listObjectsRequest); | |
List<ObjectSummary> objectSummaries = listObjectsResponse.getListObjects().getObjects(); | |
for (ObjectSummary objectSummary : objectSummaries) { | |
log.debug("Object: " + objectSummary.getName() + ", "+ objectSummary); | |
log.debug( getObjectAccessUrl(namespaceName, bucketName, objectSummary.getName(), objectSummary.getName()) ); | |
} | |
} | |
public String getObjectAccessUrl(String namespaceName, String bucketName, String name, String objectName ) throws Exception { | |
return urlPrefix + getFileObject(namespaceName, bucketName, name, objectName ).getPreauthenticatedRequest().getAccessUri(); | |
} | |
private final String urlPrefix = "https://cnsmy6pwoy50.objectstorage.ap-seoul-1.oci.customer-oci.com"; | |
public CreatePreauthenticatedRequestResponse getFileObject(String namespaceName, String bucketName, String name, String objectName) throws Exception{ | |
// Build request details | |
CreatePreauthenticatedRequestDetails createPreauthenticatedRequestDetails = | |
CreatePreauthenticatedRequestDetails.builder() | |
.name(name) | |
.bucketListingAction(PreauthenticatedRequest.BucketListingAction.ListObjects) | |
//name of a file available in object storage | |
.objectName(objectName) | |
//readonly access | |
.accessType(CreatePreauthenticatedRequestDetails.AccessType.ObjectRead) | |
//here we set expiration time as 1 hour | |
.timeExpires(Date.from(Instant.now().plusSeconds(3600))) | |
.build(); | |
//Build request | |
CreatePreauthenticatedRequestRequest createPreauthenticatedRequestRequest = CreatePreauthenticatedRequestRequest.builder() | |
.namespaceName(namespaceName) | |
.bucketName(bucketName) | |
.createPreauthenticatedRequestDetails(createPreauthenticatedRequestDetails) | |
.opcClientRequestId(UUID.randomUUID().toString()).build(); | |
// send request to oci | |
CreatePreauthenticatedRequestResponse response = client.createPreauthenticatedRequest(createPreauthenticatedRequestRequest); | |
client.close(); | |
return response; | |
} | |
} |
생성된 URL 은 1시간 동안 인증이 유효하며 사전 인증 요청들은 OCI 콘솔 > 스토이지 > 오브젝트 스토리지 및 아카이브 스토리지 > 버킷 > 버킷 세부정보의 좌측 리소스 목록중 사전인증된요청 을 클릭하면 내역을 확인할 수 있다.
댓글 없음:
댓글 쓰기