001/* 002 * JDrupes Builder 003 * Copyright (C) 2025, 2026 Michael N. Lipp 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <https://www.gnu.org/licenses/>. 017 */ 018 019package org.jdrupes.builder.api; 020 021import java.io.StringWriter; 022import java.util.Optional; 023 024/// Represents an exception that occurs during the build. Terminates the 025/// current build when thrown. 026/// 027@SuppressWarnings("serial") 028public class BuildException extends RuntimeException { 029 030 /// The reason the build failed. 031 @SuppressWarnings("PMD.FieldNamingConventions") 032 public enum Reason { 033 /// A requested resource is unavailable, because transforming one 034 /// kind of resource into another kind of resource failed due 035 /// to an inconsistent state of the resources. It is up to the 036 /// provider to emit messages that describe the reason for 037 /// the failure. 038 Unavailable, 039 040 /// There is a problem with the build configuration. The exception's 041 /// message and optional details should provide more information. 042 Configuration, 043 044 /// There is an unexpected problem with the build process, typically 045 /// due to an exception that cannot be handled and which should 046 /// be added as [#cause]. The [BuildException] will be reported to 047 /// the user with the stack trace. 048 Error 049 } 050 051 /// The reason. 052 private final Reason reason; 053 /// The message. 054 private String message; 055 /// The resource provider. 056 private ResourceProvider resourceProvider; 057 /// The details. 058 private final StringWriter details = new StringWriter(); 059 060 /// Initializes a new build exception with its reason set to 061 /// [BuildException.Reason#Error]. 062 /// 063 public BuildException() { 064 reason = Reason.Error; 065 } 066 067 /// Initializes a new build exception with the given reason. 068 /// 069 /// @param reason the reason 070 /// 071 protected BuildException(Reason reason) { 072 this.reason = reason; 073 } 074 075 /// Returns the reason why the build failed. 076 /// 077 /// @return the reason 078 /// 079 public Reason reason() { 080 return reason; 081 } 082 083 /// Sets the message of the build exception. As a convenience, any 084 /// Throwable arguments will be replaced by their message. 085 /// 086 /// @param format the format 087 /// @param args the args 088 /// @return the build exception 089 /// 090 public BuildException message(String format, Object... args) { 091 for (int i = 0; i < args.length; i++) { 092 if (args[i] instanceof Throwable) { 093 args[i] = ((Throwable) args[i]).getMessage(); 094 } 095 } 096 var message = String.format(format, args); 097 this.message = message; 098 return this; 099 } 100 101 @Override 102 public String getMessage() { 103 return message; 104 } 105 106 /// Sets the cause. 107 /// 108 /// @param cause the cause 109 /// @return the builds the exception 110 /// 111 public BuildException cause(Throwable cause) { 112 initCause(cause); 113 return this; 114 } 115 116 /// From. 117 /// 118 /// @param resourceProvider the resource provider 119 /// @return the builds the exception 120 /// 121 public BuildException from(ResourceProvider resourceProvider) { 122 this.resourceProvider = resourceProvider; 123 return this; 124 } 125 126 /// Add information to the details. 127 /// 128 /// @param detail the detail 129 /// @return the exception 130 /// 131 public BuildException detail(String detail) { 132 details.append(detail); 133 return this; 134 } 135 136 /// Returns the origin of this exception. This is the resource 137 /// provider set with [#from]. 138 /// 139 /// @return the resource provider 140 /// 141 public Optional<ResourceProvider> origin() { 142 return Optional.ofNullable(resourceProvider); 143 } 144 145 /// Return the collected details. 146 /// 147 /// @return the string 148 /// 149 public String details() { 150 details.flush(); 151 return details.toString(); 152 } 153}