se.idsec.signservice.integration.core.FileResource Maven / Gradle / Ivy
/*
* Copyright 2019-2024 IDsec Solutions AB
*
* 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 se.idsec.signservice.integration.core;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import jakarta.annotation.PostConstruct;
import lombok.Builder;
import java.io.IOException;
import java.io.Serial;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
* A {@code FileResource} class is a generic class for handling file resources used in configuration of a SignService
* Integration Service. Depending on how the service is configured a {@code FileResource} can be set up in two different
* ways:
*
* - By giving the contents of the file resource (using {@link #setContents(String)}).
* - By giving a file resource string pointing at the file resource (see {@link #setResource(String)}). This is
* typically the way a SignService Integration Service wants to configure its resources.
*
*
* @author Martin Lindström ([email protected])
* @author Stefan Santesson ([email protected])
*/
@Builder
@JsonInclude(Include.NON_NULL)
public class FileResource implements Serializable {
@Serial
private static final long serialVersionUID = 6763043655938027208L;
/** The base64 encoded contents of the file resource. */
private String contents;
/** Optional descriptive string for the file resource. */
private String description;
/** The resource string for the file/resource. */
@JsonIgnore
private String resource;
/**
* If the {@code DefaultFileResource} object is initialized by a resource string the object can function in two modes;
* it either loads the contents directly when the object is created using {@link #afterPropertiesSet()}
* ({@code eagerlyLoadContents = true}), or it loads the contents every time it is asked for
* ({@code eagerlyLoadContents = false}). The latter is the default and should be used if large documents that are
* assigned to several configuration objects are handled. This will prevent a heavy memory usage at the cost of speed
* in fetching the document contents.
*/
@Builder.Default
@JsonIgnore
private boolean eagerlyLoadContents = false;
/**
* Default constructor.
*/
public FileResource() {
}
/**
* Constructor.
*
* @param contents the base64 encoded contents of the file resource
* @param description optional descriptive string for the file resource
* @param resource the resource string for the file/resource
* @param eagerlyLoadContents whether to load contents directly (see {@link #setEagerlyLoadContents(boolean)})
*/
public FileResource(
final String contents, final String description, final String resource, final boolean eagerlyLoadContents) {
this.contents = contents;
this.description = description;
this.resource = resource;
this.eagerlyLoadContents = eagerlyLoadContents;
}
/**
* Gets the contents of the file resource as a base64 encoded string.
*
* @return the file resource content as a base64 encoded string or null if no content is available
*/
public String getContents() {
if (this.contents == null && this.resource != null) {
final String _contents = loadContentsFromResource(this.resource);
if (this.eagerlyLoadContents) {
// It seems like afterPropertiesSet didn't do its job, let's save the contents
this.contents = _contents;
}
return _contents;
}
return this.contents;
}
/**
* Assigns the file resource contents as a base64 encoded string.
*
* @param contents the base64 encoded contents of the file resource
*/
public void setContents(final String contents) {
// Assert that the contents is a valid Base64 encoding (will throw if not) ...
Base64.getDecoder().decode(contents);
this.contents = contents;
}
/**
* Assigns the raw file resource contents.
*
* @param contents the raw file resource contents
*/
public void setContents(final byte[] contents) {
this.setContents(new String(Base64.getEncoder().encode(contents), StandardCharsets.UTF_8));
}
/**
* Gets the description of the file resource.
*
* @return the description of the file resource or null if none is available
*/
public String getDescription() {
return this.description;
}
/**
* Assigns the description of the file resource.
*
* @param description the description of the file resource
*/
public void setDescription(final String description) {
this.description = description;
}
/**
* If the {@code FileResource} object was initialized with a {@code resource} string this method returns this string.
*
* @return the file resource string or null if none is set
*/
public String getResource() {
return this.resource;
}
/**
* Assigns a resource string.
*
*
* Note: The Spring Framework style of representing a resource should be used. For example: {@code classpath:xyz.svg}
* and {@code file:/path/xyz.svg}.
*
*
* @param resource the resource string
*/
public void setResource(final String resource) {
this.resource = resource;
}
/**
* If the {@code FileResource} object is initialized by a resource string the object can function in two modes; it
* either loads the contents directly when the object is created using {@link #afterPropertiesSet()}
* ({@code eagerlyLoadContents = true}), or it loads the contents every time it is asked for
* ({@code eagerlyLoadContents = false}). The latter is the default and should be used if large documents that are
* assigned to several configuration objects are handled. This will prevent a heavy memory usage at the cost of speed
* in fetching the document contents.
*
* @param eagerlyLoadContents whether to load contents eagerly or not
*/
public void setEagerlyLoadContents(final boolean eagerlyLoadContents) {
this.eagerlyLoadContents = eagerlyLoadContents;
}
/**
* Checks that the file resource is correctly initialized, and if {@code eagerlyLoadContents} is {@code true} it also
* loads the contents (if necessary).
*
*
* Note: If executing in a Spring Framework environment this method is automatically invoked after all properties have
* been assigned. Otherwise it should be explicitly invoked.
*
*
* @throws Exception for init errors
*/
@PostConstruct
public void afterPropertiesSet() throws Exception {
if (this.contents == null && this.resource == null) {
throw new Exception("Either contents or resource must be set");
}
if (this.contents != null && this.resource != null) {
// Both contents and resource has been set for DefaultFileResource, resource string will be ignored
this.resource = null;
}
if (this.contents == null && this.resource != null && this.eagerlyLoadContents) {
this.contents = loadContentsFromResource(this.resource);
this.resource = null;
}
}
/**
* Helper method that loads contents from a resource.
*
* @param resource the resource string
* @return the Base64 encoded contents
*/
private static String loadContentsFromResource(final String resource) {
if (resource != null) {
try {
final byte[] contents = ContentLoaderSingleton.getInstance().loadContent(resource);
return Base64.getEncoder().encodeToString(contents);
}
catch (final IOException e) {
final String msg = String.format("Failed to load contents from '%s' - %s", resource, e.getMessage());
throw new RuntimeException(msg, e);
}
}
return null;
}
/** {@inheritDoc} */
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("contents=");
if (this.contents != null) {
builder.append("{size=").append(this.contents.length()).append("}");
}
else {
builder.append("");
}
if (this.description != null) {
builder.append(",description=\"").append(this.description).append("\"");
}
if (this.resource != null) {
builder.append(",resource=\"").append(this.resource).append("\"");
}
builder.append(",eagerlyLoadContents=").append(this.eagerlyLoadContents);
return builder.toString();
}
/**
* Builder class for {@link FileResource}.
*/
public static class FileResourceBuilder implements ObjectBuilder {
@SuppressWarnings("unused")
private final boolean eagerlyLoadContents = false;
// Lombok
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy