org.terracotta.utilities.test.io.WindowsSpecialFolder Maven / Gradle / Ivy
/*
* Copyright 2020 Terracotta, Inc., a Software AG company.
* Copyright Super iPaaS Integration LLC, an IBM Company 2024
*
* Licensed 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.terracotta.utilities.test.io;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terracotta.utilities.exec.Shell;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import static java.util.Objects.requireNonNull;
/**
* Provides the value for Windows Special Folder identifiers.
*
* The values represented by this enumeration are determined when first referenced
* by calling {@code PowerShell} to obtain the path assigned to the special folder
* identifier.
*
* @see
* Environment.SpecialFolder Enum
* @see
* Environment.GetFolderPath Method
*/
public enum WindowsSpecialFolder {
/**
* Provides the {@code Path} corresponding to the {@code CommonApplicationData} special folder.
* A get via this constant will attempt to create the folder if it does not already exist.
*/
COMMON_APPLICATION_DATA("CommonApplicationData", true),
/**
* Provides the {@code Path} corresponding to the {@code System} special folder.
*/
SYSTEM("System", false);
private final LazyProperty accessor;
WindowsSpecialFolder(String identifier, boolean create) {
requireNonNull(identifier, "identifier");
this.accessor = LazyProperty.lazily(() -> getSpecialFolder(identifier, create));
}
/**
* Gets the {@code Path} assigned to the identified special folder.
* @return the special folder {@code Path}
* @throws IOException if an error is raised while attempting to determine the
* {@code Path} for the special folder
*/
public Path get() throws IOException {
return accessor.get();
}
private static final Logger LOGGER = LoggerFactory.getLogger(WindowsSpecialFolder.class);
/**
* Gets the {@link Path} to the identified Windows Special Folder.
*
* @param specialFolderId the special folder identifier
* @param create if {@code true}, attempts to create the folder if it does not exist
* @return the path to the special folder; an empty path is returned if the folder does not exist
* @throws IOException if the special folder value cannot be determined
*/
private static Path getSpecialFolder(String specialFolderId, boolean create) throws IOException {
String[] command = new String[] {
"powershell.exe",
"-NoLogo",
"-NoProfile",
"-NonInteractive",
"-Command",
"&{$ErrorActionPreference = 'Stop'; " +
"[environment]::getfolderpath('" + specialFolderId + "'" + (create ? ", 'create'" : "") + ")}" };
String specialFolder;
Shell.Result result;
try {
result = Shell.execute(Shell.Encoding.CHARSET, command);
} catch (IOException e) {
LOGGER.error("Unable to determine special folder for {}; {} failed",
specialFolderId, Arrays.toString(command), e);
throw e;
}
if (result.exitCode() == 0) {
specialFolder = result.lines().get(0);
} else {
SpecialFolderException exception =
new SpecialFolderException(specialFolderId, result.lines(), result.exitCode());
LOGGER.error("Unable to determine special folder for {}", specialFolderId, exception);
throw exception;
}
return Paths.get(specialFolder);
}
/**
* A derived on first reference value holder.
* @param the value type
*/
private static final class LazyProperty {
public static LazyProperty lazily(ThrowingSupplier from) {
return new LazyProperty<>(from);
}
private final AtomicReference reference = new AtomicReference<>();
private final ThrowingSupplier supplier;
private LazyProperty(ThrowingSupplier supplier) {
this.supplier = supplier;
}
public T get() throws IOException {
T t;
do {
t = reference.get();
} while (t == null && !reference.compareAndSet(null, t = supplier.get()));
return t;
}
@FunctionalInterface
public interface ThrowingSupplier {
T get() throws IOException;
}
}
/**
* Thrown when the folder assigned to a Windows Special Folder identifier cannot be determined.
*/
public static class SpecialFolderException extends IOException {
private static final long serialVersionUID = 8319003610562205355L;
private final int code;
private SpecialFolderException(String specialFolder, List errorDetail, int code) {
super("Error determining directory assigned to special folder \"" + specialFolder + "\"; rc=" + code
+ (errorDetail == null ? "" : "\n " + String.join("\n ", errorDetail)));
this.code = code;
}
public int code() {
return code;
}
}
}