Skip to content

Commit

Permalink
refactor: introduce commons module (#251)
Browse files Browse the repository at this point in the history
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Joseph Sinclair <[email protected]>
Co-authored-by: Joseph Sinclair <[email protected]>
  • Loading branch information
ata-nas and jsync-swirlds authored Oct 16, 2024
1 parent c2a360a commit 1e0288c
Show file tree
Hide file tree
Showing 8 changed files with 360 additions and 2 deletions.
25 changes: 25 additions & 0 deletions buildSrc/src/main/kotlin/com.hedera.block.common.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2024 Hedera Hashgraph, LLC
*
* 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.
*/

plugins {
id("java-library")
id("com.hedera.block.conventions")
id("me.champeau.jmh")
}

val maven = publishing.publications.create<MavenPublication>("maven") { from(components["java"]) }

signing.sign(maven)
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,18 @@ plugins { id("com.diffplug.spotless") }
spotless {
java {
targetExclude("build/generated/**/*.java", "build/generated/**/*.proto")
// enable toggle comment support
// Enables the spotless:on and spotless:off comments
toggleOffOn()
// don't need to set target, it is inferred from java
// apply a specific flavor of google-java-format
googleJavaFormat("1.17.0").aosp().reflowLongStrings()
// also reflow long strings, and do not format javadoc
// because the default setup is _very_ bad for javadoc
// We need to figure out a "correct" _separate_ setup for that.
googleJavaFormat("1.17.0").aosp().reflowLongStrings().formatJavadoc(false)
// Fix some left-out items from the google plugin
indentWithSpaces(4)
trimTrailingWhitespace()
endWithNewline()
// make sure every file has the following copyright header.
// optionally, Spotless can set copyright years by digging
// through git history (see "license" section below).
Expand Down
24 changes: 24 additions & 0 deletions common/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2024 Hedera Hashgraph, LLC
*
* 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.
*/

plugins {
id("java-library")
id("com.hedera.block.common")
}

description = "Commons module with logic that could be abstracted and reused."

testModuleInfo { requiresStatic("com.github.spotbugs.annotations") }
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2024 Hedera Hashgraph, LLC
*
* 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 com.hedera.block.common.constants;

/** A class that hold common String literals used across projects. */
public final class StringsConstants {
/**
* File name for application properties
*/
public static final String APPLICATION_PROPERTIES = "app.properties";

/**
* File name for logging properties
*/
public static final String LOGGING_PROPERTIES = "logging.properties";

private StringsConstants() {}
}
221 changes: 221 additions & 0 deletions common/src/main/java/com/hedera/block/common/utils/FileUtilities.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
/*
* Copyright (C) 2024 Hedera Hashgraph, LLC
*
* 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 com.hedera.block.common.utils;

import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.IOException;
import java.lang.System.Logger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Objects;
import java.util.Set;
import java.util.zip.GZIPInputStream;

/** A utility class that deals with logic related to dealing with files. */
public final class FileUtilities {
private static final Logger LOGGER = System.getLogger(FileUtilities.class.getName());

/**
* The default file permissions for new files.
* <p>
* Default permissions are set to: rw-r--r--
*/
private static final FileAttribute<Set<PosixFilePermission>> DEFAULT_FILE_PERMISSIONS =
PosixFilePermissions.asFileAttribute(
Set.of(
PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.GROUP_READ,
PosixFilePermission.OTHERS_READ));

/**
* Default folder permissions for new folders.
* <p>
* Default permissions are set to: rwxr-xr-x
*/
private static final FileAttribute<Set<PosixFilePermission>> DEFAULT_FOLDER_PERMISSIONS =
PosixFilePermissions.asFileAttribute(
Set.of(
PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.OWNER_EXECUTE,
PosixFilePermission.GROUP_READ,
PosixFilePermission.GROUP_EXECUTE,
PosixFilePermission.OTHERS_READ,
PosixFilePermission.OTHERS_EXECUTE));

/**
* Log message template used when a path is not created because a file
* or folder already exists at the requested path.
*/
private static final String PRE_EXISTING_FOLDER_MESSAGE =
"Requested %s [%s] not created because %s already exists at %s";

/**
* Create a new path (folder or file) if it does not exist.
* Any folders or files created will use default permissions.
*
* @param toCreate valid, non-null instance of {@link Path} to be created
* @param logLevel valid, non-null instance of {@link System.Logger.Level} to use
* @param semanticPathName valid, non-blank {@link String} used for logging that represents the
* desired path semantically
* @param createDir {@link Boolean} value if we should create a directory or a file
* @throws IOException if the path cannot be created
*/
public static void createPathIfNotExists(
@NonNull final Path toCreate,
@NonNull final System.Logger.Level logLevel,
@NonNull final String semanticPathName,
final boolean createDir)
throws IOException {
createPathIfNotExists(
toCreate,
logLevel,
DEFAULT_FILE_PERMISSIONS,
DEFAULT_FOLDER_PERMISSIONS,
semanticPathName,
createDir);
}

/**
* Create a new path (folder or file) if it does not exist.
*
* @param toCreate The path to be created.
* @param logLevel The logging level to use when logging this event.
* @param filePermissions Permissions to use when creating a new file.
* @param folderPermissions Permissions to use when creating a new folder.
* @param semanticPathName A name to represent the path in a logging
* statement.
* @param createDir A flag indicating we should create a directory
* (true) or a file (false)
* @throws IOException if the path cannot be created due to a filesystem
* error.
*/
public static void createPathIfNotExists(
@NonNull final Path toCreate,
@NonNull final System.Logger.Level logLevel,
@NonNull final FileAttribute<Set<PosixFilePermission>> filePermissions,
@NonNull final FileAttribute<Set<PosixFilePermission>> folderPermissions,
@NonNull final String semanticPathName,
final boolean createDir)
throws IOException {
Objects.requireNonNull(toCreate);
Objects.requireNonNull(logLevel);
Objects.requireNonNull(filePermissions);
Objects.requireNonNull(folderPermissions);
StringUtilities.requireNotBlank(semanticPathName);
final String requestedType = createDir ? "directory" : "file";
if (Files.notExists(toCreate)) {
if (createDir) {
Files.createDirectories(toCreate, folderPermissions);
} else {
Files.createFile(toCreate, filePermissions);
}
final String logMessage =
"Created %s [%s] at %s".formatted(requestedType, semanticPathName, toCreate);
LOGGER.log(logLevel, logMessage);
} else {
final String actualType = Files.isDirectory(toCreate) ? "directory" : "file";
final String logMessage =
PRE_EXISTING_FOLDER_MESSAGE.formatted(
requestedType, semanticPathName, actualType, toCreate);
LOGGER.log(logLevel, logMessage);
}
}

/**
* Read a GZIP file and return the content as a byte array.
* <p>
* This method is _unsafe_ because it reads the entire file content into
* a single byte array, which can cause memory issues, and may fail if the
* file contains a large amount of data.
*
* @param filePath Path to the GZIP file.
* @return byte array containing the _uncompressed_ content of the GZIP file.
* @throws IOException if unable to read the file.
* @throws OutOfMemoryError if a byte array large enough to contain the
* file contents cannot be allocated (either because it exceeds MAX_INT
* bytes or exceeds available heap memory).
*/
public static byte[] readGzipFileUnsafe(@NonNull final Path filePath) throws IOException {
Objects.requireNonNull(filePath);
try (final var gzipInputStream = new GZIPInputStream(Files.newInputStream(filePath))) {
return gzipInputStream.readAllBytes();
}
}

/**
* Read a file and return the content as a byte array.
* <p>
* This method uses default extensions for gzip and block files.
* <p>
* This method is _unsafe_ because it reads the entire file content into
* a single byte array, which can cause memory issues, and may fail if the
* file contains a large amount of data.
*
* @param filePath Path to the file
* @return byte array of the content of the file or null if the file extension is not
* supported
* @throws IOException if unable to read the file.
* @throws OutOfMemoryError if a byte array large enough to contain the
* file contents cannot be allocated (either because it exceeds MAX_INT
* bytes or exceeds available heap memory).
*/
public static byte[] readFileBytesUnsafe(@NonNull final Path filePath) throws IOException {
return readFileBytesUnsafe(filePath, ".blk", ".gz");
}

/**
* Read a file and return the content as a byte array.
* <p>
* This method is _unsafe_ because it reads the entire file content into
* a single byte array, which can cause memory issues, and may fail if the
* file contains a large amount of data.
*
* @param filePath Path to the file to read.
* @param blockFileExtension A file extension for block files.
* @param gzipFileExtension A file extension for gzip files.
* @return A byte array with the full contents of the file, or null if the
* file extension requested does not match at least one of the
* extensions provided (GZip or Block).
* @throws IOException if unable to read the file.
* @throws OutOfMemoryError if a byte array large enough to contain the
* file contents cannot be allocated (either because it exceeds MAX_INT
* bytes or exceeds available heap memory).
*/
public static byte[] readFileBytesUnsafe(
@NonNull final Path filePath,
@NonNull final String blockFileExtension,
@NonNull final String gzipFileExtension)
throws IOException {
final String filePathAsString = Objects.requireNonNull(filePath).toString();
Objects.requireNonNull(blockFileExtension);
Objects.requireNonNull(gzipFileExtension);
if (filePathAsString.endsWith(gzipFileExtension)) {
return readGzipFileUnsafe(filePath);
} else if (filePathAsString.endsWith(blockFileExtension)) {
return Files.readAllBytes(filePath);
} else {
return null;
}
}

private FileUtilities() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2024 Hedera Hashgraph, LLC
*
* 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 com.hedera.block.common.utils;

import edu.umd.cs.findbugs.annotations.NonNull;
import java.util.Objects;

/** A utility class that deals with logic related to Strings. */
public final class StringUtilities {
/**
* This method checks if a given {@link String} is blank, meaning if it is {@code null} or
* contains only whitespaces as defined by {@link String#isBlank()}. If the given {@link String}
* is not blank, then we return it, else we throw {@link IllegalArgumentException}.
*
* @param toCheck a {@link String} to be checked if is blank as defined above
* @return the {@link String} to be checked if it is not blank as defined above
* @throws IllegalArgumentException if the input {@link String} to be checked is blank
*/
public static String requireNotBlank(@NonNull final String toCheck) {
if (Objects.requireNonNull(toCheck).isBlank()) {
throw new IllegalArgumentException("A String required to be non-blank is blank.");
} else {
return toCheck;
}
}

private StringUtilities() {}
}
6 changes: 6 additions & 0 deletions common/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module com.hedera.block.common {
exports com.hedera.block.common.constants;
exports com.hedera.block.common.utils;

requires static com.github.spotbugs.annotations;
}
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ plugins {
}

// Include the subprojects
include(":common")
include(":suites")
include(":stream")
include(":server")
Expand Down

0 comments on commit 1e0288c

Please sign in to comment.