001/*
002 * acme4j - Java ACME client
003 *
004 * Copyright (C) 2016 Richard "Shred" Körber
005 *   http://acme4j.shredzone.org
006 *
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
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.
013 */
014package org.shredzone.acme4j;
015
016import java.io.Serial;
017import java.io.Serializable;
018import java.net.URL;
019import java.util.Objects;
020
021import edu.umd.cs.findbugs.annotations.Nullable;
022
023/**
024 * This is the root class of all ACME resources (like accounts, orders, certificates).
025 * Every resource is identified by its location URL.
026 * <p>
027 * This class also takes care for proper serialization and de-serialization of the
028 * resource. After de-serialization, the resource must be bound to a {@link Login} again,
029 * using {@link #rebind(Login)}.
030 */
031public abstract class AcmeResource implements Serializable {
032    @Serial
033    private static final long serialVersionUID = -7930580802257379731L;
034
035    private transient @Nullable Login login;
036    private final URL location;
037
038    /**
039     * Create a new {@link AcmeResource}.
040     *
041     * @param login
042     *            {@link Login} the resource is bound with
043     * @param location
044     *            Location {@link URL} of this resource
045     */
046    protected AcmeResource(Login login, URL location) {
047        this.location = Objects.requireNonNull(location, "location");
048        rebind(login);
049    }
050
051    /**
052     * Gets the {@link Login} this resource is bound with.
053     */
054    protected Login getLogin() {
055        if (login == null) {
056            throw new IllegalStateException("Use rebind() for binding this object to a login.");
057        }
058        return login;
059    }
060
061    /**
062     * Gets the {@link Session} this resource is bound with.
063     */
064    protected Session getSession() {
065        return getLogin().getSession();
066    }
067
068    /**
069     * Rebinds this resource to a {@link Login}.
070     * <p>
071     * Logins are not serialized, because they contain volatile session data and also a
072     * private key. After de-serialization of an {@link AcmeResource}, use this method to
073     * rebind it to a {@link Login}.
074     *
075     * @param login
076     *            {@link Login} to bind this resource to
077     */
078    public void rebind(Login login) {
079        if (this.login != null) {
080            throw new IllegalStateException("Resource is already bound to a login");
081        }
082        this.login = Objects.requireNonNull(login, "login");
083    }
084
085    /**
086     * Gets the resource's location.
087     */
088    public URL getLocation() {
089        return location;
090    }
091
092    @Override
093    protected final void finalize() {
094        // CT_CONSTRUCTOR_THROW: Prevents finalizer attack
095    }
096
097}