001/*
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2023, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014
015package ch.qos.logback.core.model.processor;
016
017import ch.qos.logback.core.Context;
018import ch.qos.logback.core.model.Model;
019import ch.qos.logback.core.model.SerializeModelModel;
020
021import java.io.FileOutputStream;
022import java.io.IOException;
023import java.io.ObjectOutputStream;
024import java.time.Instant;
025import java.time.format.DateTimeFormatter;
026
027import static ch.qos.logback.core.CoreConstants.FILE_TIMESTAMP_PATTERN;
028import static ch.qos.logback.core.CoreConstants.MODEL_CONFIG_FILE_EXTENSION;
029
030public class SerializeModelModelHandler extends ModelHandlerBase {
031
032    public SerializeModelModelHandler(Context context) {
033        super(context);
034    }
035
036    static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext mic) {
037        return new SerializeModelModelHandler(context);
038    }
039
040    @Override
041    public void handle(ModelInterpretationContext modelInterpretationContext, Model model)
042            throws ModelHandlerException {
043
044
045        Object configuratorHint = modelInterpretationContext.getConfiguratorHint();
046
047        if(configuratorHint != null && configuratorHint.getClass().getName().equals("ch.qos.logback.classic.joran.SerializedModelConfigurator")) {
048            addInfo("Skipping model serialization as calling configurator is already model based.");
049            return;
050        }
051
052        if (!(model instanceof SerializeModelModel)) {
053            addWarn("Model parameter is not of type SerializeModelModel. Skipping serialization of model structure");
054            return;
055        }
056
057        SerializeModelModel serializeModelModel = (SerializeModelModel) model;
058
059        Model topModel = modelInterpretationContext.getTopModel();
060
061        if (topModel == null) {
062            addWarn("Could not find top most model. Skipping serialization of model structure.");
063            return;
064        }
065
066        String fileStr = serializeModelModel.getFile();
067        if (fileStr == null) {
068            DateTimeFormatter dft = DateTimeFormatter.ofPattern(FILE_TIMESTAMP_PATTERN);
069            Instant now = Instant.now();
070            String timestamp = dft.format(now);
071            fileStr = "logback-" + timestamp + MODEL_CONFIG_FILE_EXTENSION;
072            addInfo("For model serialization, using default file destination [" + fileStr + "]");
073        } else {
074            fileStr = modelInterpretationContext.subst(fileStr);
075        }
076
077        writeModel(fileStr, topModel);
078    }
079
080    private void writeModel(String fileStr, Model firstModel) {
081
082        addInfo("Serializing model to file ["+fileStr+"]");
083
084        try (FileOutputStream fos = new FileOutputStream(fileStr)) {
085            ObjectOutputStream oos = new ObjectOutputStream(fos);
086            oos.writeObject(firstModel);
087            oos.flush();
088            oos.close();
089        } catch (IOException e) {
090            addError("IO failure while serializing Model ["+fileStr+"]");
091        }
092    }
093}