    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements. See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache license, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License. You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the license for the specific language governing permissions and
    015     * limitations under the license.
    016     */
    017    package org.apache.logging.log4j.core.net;
    018
    019    import java.io.IOException;
    020    import java.io.OutputStream;
    021    import java.net.DatagramPacket;
    022    import java.net.DatagramSocket;
    023    import java.net.InetAddress;
    024    import java.net.SocketException;
    025    import java.net.UnknownHostException;
    026
    027    import org.apache.logging.log4j.Logger;
    028    import org.apache.logging.log4j.core.appender.AppenderLoggingException;
    029    import org.apache.logging.log4j.status.StatusLogger;
    030
    031    /**
    032     * OutputStream for UDP connections.
    033     */
    034    public class DatagramOutputStream extends OutputStream {
    035
    036        /**
    037         * Allow subclasses access to the status logger without creating another instance.
    038         */
    039        protected static final Logger LOGGER = StatusLogger.getLogger();
    040
    041        private static final int SHIFT_1 = 8;
    042        private static final int SHIFT_2 = 16;
    043        private static final int SHIFT_3 = 24;
    044
    045        private DatagramSocket ds;
    046        private final InetAddress address;
    047        private final int port;
    048
    049        private byte[] data;
    050
    051        private final byte[] header;
    052        private final byte[] footer;
    053
    054        /**
    055         * The Constructor.
    056         * @param host The host to connect to.
    057         * @param port The port on the host.
    058         */
    059        public DatagramOutputStream(final String host, final int port, final byte[] header, final byte[] footer) {
    060            this.port = port;
    061            this.header = header;
    062            this.footer = footer;
    063            try {
    064                address = InetAddress.getByName(host);
    065            } catch (final UnknownHostException ex) {
    066                final String msg = "Could not find host " + host;
    067                LOGGER.error(msg, ex);
    068                throw new AppenderLoggingException(msg, ex);
    069            }
    070
    071            try {
    072                ds = new DatagramSocket();
    073            } catch (final SocketException ex) {
    074                final String msg = "Could not instantiate DatagramSocket to " + host;
    075                LOGGER.error(msg, ex);
    076                throw new AppenderLoggingException(msg, ex);
    077            }
    078        }
    079
    080        @Override
    081        public synchronized void write(final byte[] bytes, final int offset, final int length) throws IOException {
    082            copy(bytes, offset, length);
    083        }
    084
    085        @Override
    086        public synchronized void write(final int i) throws IOException {
    087            copy(new byte[] {(byte) (i >>> SHIFT_3), (byte) (i >>> SHIFT_2), (byte) (i >>> SHIFT_1), (byte) i}, 0, 4);
    088        }
    089
    090        @Override
    091        public synchronized void write(final byte[] bytes) throws IOException {
    092            copy(bytes, 0, bytes.length);
    093        }
    094
    095        @Override
    096        public synchronized void flush() throws IOException {
    097            try {
    098                if (this.data != null && this.ds != null && this.address != null) {
    099                    if (footer != null) {
    100                        copy(footer, 0, footer.length);
    101                    }
    102                    final DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
    103                    ds.send(packet);
    104                }
    105            } finally {
    106                data = null;
    107                if (header != null) {
    108                    copy(header, 0, header.length);
    109                }
    110            }
    111        }
    112
    113        @Override
    114        public synchronized void close() throws IOException {
    115            if (ds != null) {
    116                if (data != null) {
    117                    flush();
    118                }
    119                ds.close();
    120                ds = null;
    121            }
    122        }
    123
    124        private void copy(final byte[] bytes, final int offset, final int length) {
    125            final int index = data == null ? 0 : data.length;
    126            final byte[] copy = new byte[length + index];
    127            if (data != null) {
    128                System.arraycopy(data, 0, copy, 0, data.length);
    129            }
    130            System.arraycopy(bytes, offset, copy, index, length);
    131            data = copy;
    132        }
    133    }