001/*
002 * JGrapes Event Driven Framework
003 * Copyright (C) 2022 Michael N. Lipp
004 * 
005 * This program is free software; you can redistribute it and/or modify it 
006 * under the terms of the GNU General Public License as published by 
007 * the Free Software Foundation; either version 3 of the License, or 
008 * (at your option) any later version.
009 * 
010 * This program is distributed in the hope that it will be useful, but 
011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
013 * for more details.
014 * 
015 * You should have received a copy of the GNU General Public License along 
016 * with this program; if not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jgrapes.mail;
020
021import java.util.List;
022import java.util.Map;
023import java.util.Optional;
024import java.util.Properties;
025import org.jgrapes.core.Channel;
026import org.jgrapes.core.Component;
027import org.jgrapes.core.Components;
028import org.jgrapes.core.Event;
029import org.jgrapes.core.Manager;
030import org.jgrapes.core.annotation.Handler;
031import org.jgrapes.core.annotation.HandlerDefinition.ChannelReplacements;
032import org.jgrapes.util.Password;
033import org.jgrapes.util.events.ConfigurationUpdate;
034
035/**
036 * A base class for mail handling components.
037 */
038public abstract class MailComponent extends Component {
039
040    protected final Properties mailProps = new Properties();
041    private Password password;
042
043    /**
044     * Creates a new component with its channel set to itself.
045     */
046    public MailComponent() {
047        // Nothing to do.
048    }
049
050    /**
051     * Creates a new component base with its channel set to the given 
052     * channel. As a special case {@link Channel#SELF} can be
053     * passed to the constructor to make the component use itself
054     * as channel. The special value is necessary as you 
055     * obviously cannot pass an object to be constructed to its 
056     * constructor.
057     *
058     * @param componentChannel the channel that the component's
059     * handlers listen on by default and that 
060     * {@link Manager#fire(Event, Channel...)} sends the event to
061     */
062    public MailComponent(Channel componentChannel) {
063        super(componentChannel);
064    }
065
066    /**
067     * Creates a new component base like {@link #MailComponent(Channel)}
068     * but with channel mappings for {@link Handler} annotations.
069     *
070     * @param componentChannel the channel that the component's
071     * handlers listen on by default and that 
072     * {@link Manager#fire(Event, Channel...)} sends the event to
073     * @param channelReplacements the channel replacements to apply
074     * to the `channels` elements of the {@link Handler} annotations
075     */
076    public MailComponent(Channel componentChannel,
077            ChannelReplacements channelReplacements) {
078        super(componentChannel, channelReplacements);
079    }
080
081    /**
082     * Sets the mail properties. See 
083     * [the Jakarta Mail](https://jakarta.ee/specifications/mail/2.0/apidocs/jakarta.mail/jakarta/mail/package-summary.html)
084     * documentation for available settings. The given properties are
085     * merged with the already existing properties.
086     *
087     * @param props the props
088     * @return the mail monitor
089     */
090    public MailComponent setMailProperties(Map<String, String> props) {
091        mailProps.putAll(props);
092        return this;
093    }
094
095    /**
096     * Sets the password.
097     *
098     * @param password the new password
099     */
100    public MailComponent setPassword(Password password) {
101        this.password = password;
102        return this;
103    }
104
105    /**
106     * Return the password.
107     *
108     * @return the optional password
109     */
110    protected Optional<Password> password() {
111        return Optional.ofNullable(password);
112    }
113
114    /**
115     * Configure the component. Attempts to access all paths specified
116     * in the package description in sequence as described in 
117     * {@link org.jgrapes.mail}. For each path, merges the
118     * `mail` properties and invokes {@link #configureComponent}
119     * with the available key/value pairs.  
120     *  
121     * @param event the event
122     */
123    @Handler
124    @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
125    public void onConfigUpdate(ConfigurationUpdate event) {
126        for (var path : List.of("/org.jgrapes.mail/Component",
127            "/org.jgrapes.mail/" + getClass().getSimpleName(),
128            "/org_jgrapes_mail/Component",
129            "/org_jgrapes_mail/" + getClass().getSimpleName(),
130            componentPath())) {
131            event.values(path + "/mail").ifPresent(c -> {
132                for (var e : c.entrySet()) {
133                    mailProps.put("mail." + e.getKey(), e.getValue());
134                }
135            });
136            event.values(path).ifPresent(v -> {
137                Optional.ofNullable(v.get("password"))
138                    .ifPresent(p -> setPassword(new Password(p.toCharArray())));
139                configureComponent(v);
140            });
141        }
142    }
143
144    /**
145     * Configure the component specific values.
146     *
147     * @param values the values
148     */
149    protected abstract void configureComponent(Map<String, String> values);
150
151    /*
152     * (non-Javadoc)
153     * 
154     * @see java.lang.Object#toString()
155     */
156    @Override
157    public String toString() {
158        return Components.objectName(this);
159    }
160}