001/* 
002 * Copyright (C) 2016 Hobrasoft s.r.o.
003 *
004 * This program is free software: you can redistribute it and/or modify
005 * it under the terms of the GNU Affero General Public License as published by
006 * the Free Software Foundation, either version 3 of the License, or
007 * (at your option) any later version.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012 * GNU Affero General Public License for more details.
013 *
014 * You should have received a copy of the GNU Affero General Public License
015 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
016 */
017package cz.hobrasoft.pdfmu.operation;
018
019import cz.hobrasoft.pdfmu.PdfmuUtils;
020import cz.hobrasoft.pdfmu.WritingMapper;
021import cz.hobrasoft.pdfmu.error.ErrorType;
022import cz.hobrasoft.pdfmu.jackson.RpcError;
023import cz.hobrasoft.pdfmu.jackson.RpcError.Data;
024import cz.hobrasoft.pdfmu.jackson.RpcResponse;
025import java.io.IOException;
026import java.util.Map;
027import java.util.logging.Logger;
028import org.apache.commons.lang3.text.StrSubstitutor;
029
030/**
031 * This exception is thrown by {@link Operation#execute(Namespace)} to notify
032 * {@link cz.hobrasoft.pdfmu.Main#main(String[])} that the operation has
033 * encountered a condition it cannot recover from.
034 *
035 * @author <a href="mailto:filip.bartek@hobrasoft.cz">Filip Bartek</a>
036 */
037public class OperationException extends Exception {
038
039    private static final Logger logger = Logger.getLogger(OperationException.class.getName());
040
041    // Configuration
042    private static final int defaultErrorCode = -1;
043
044    private ErrorType errorType = null;
045    private Map<String, Object> messageArguments = null;
046
047    public OperationException(ErrorType errorType) {
048        super(errorType.toString());
049        init(errorType, null);
050    }
051
052    public OperationException(ErrorType errorType, Map.Entry<String, Object>... entries) {
053        super(errorType.toString());
054        init(errorType, PdfmuUtils.sortedMap(entries));
055    }
056
057    public OperationException(ErrorType errorType, Map<String, Object> messageArguments) {
058        super(errorType.toString());
059        init(errorType, messageArguments);
060    }
061
062    public OperationException(ErrorType errorType, Throwable cause) {
063        super(errorType.toString(), cause);
064        init(errorType, null);
065    }
066
067    public OperationException(ErrorType errorType, Throwable cause, Map.Entry<String, Object>... entries) {
068        super(errorType.toString(), cause);
069        init(errorType, PdfmuUtils.sortedMap(entries));
070    }
071
072    /**
073     * Creates a chained operation exception with error identifier and message
074     * arguments.
075     *
076     * @param errorType the error identifier.
077     * @param cause the original cause.
078     * @param messageArguments the arguments of the message.
079     */
080    public OperationException(ErrorType errorType, Throwable cause, Map<String, Object> messageArguments) {
081        super(errorType.toString(), cause);
082        init(errorType, messageArguments);
083    }
084
085    private void init(ErrorType errorType, Map<String, Object> messageArguments) {
086        assert errorType != null;
087        this.errorType = errorType;
088        this.messageArguments = messageArguments;
089    }
090
091    /**
092     * Returns the error code associated with this exception
093     *
094     * <p>
095     * The code should uniquely identify the error that caused the exception.
096     *
097     * @return the error code associated with this exception
098     */
099    public int getCode() {
100        if (errorType != null) {
101            return errorType.getCode();
102        } else {
103            return defaultErrorCode;
104        }
105    }
106
107    @Override
108    public String getLocalizedMessage() {
109        if (errorType != null) {
110            String pattern = errorType.getMessagePattern();
111            if (pattern != null) {
112                StrSubstitutor sub = new StrSubstitutor(messageArguments);
113                return sub.replace(pattern);
114            } else {
115                return super.getLocalizedMessage();
116            }
117        } else {
118            return super.getLocalizedMessage();
119        }
120    }
121
122    private RpcError getRpcError() {
123        RpcError re = new RpcError(getCode(), getLocalizedMessage());
124        Throwable cause = getCause();
125        if (cause != null || messageArguments != null) {
126            re.data = new Data();
127            if (cause != null) {
128                re.data.causeClass = cause.getClass();
129                re.data.causeMessage = cause.getLocalizedMessage();
130            }
131            re.data.arguments = messageArguments;
132        }
133        return re;
134    }
135
136    public void writeInWritingMapper(WritingMapper wm) {
137        RpcResponse response = new RpcResponse(getRpcError());
138
139        try {
140            wm.writeValue(response);
141        } catch (IOException ex) {
142            logger.severe("Could not write JSON output.");
143        }
144    }
145}