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 org.apache.logging.log4j.Logger;
020    import org.apache.logging.log4j.core.appender.AppenderRuntimeException;
021    import org.apache.logging.log4j.status.StatusLogger;
022    
023    import java.io.IOException;
024    import java.io.OutputStream;
025    import java.net.DatagramPacket;
026    import java.net.DatagramSocket;
027    import java.net.InetAddress;
028    import java.net.SocketException;
029    import java.net.UnknownHostException;
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        /**
052         * The Constructor.
053         * @param host The host to connect to.
054         * @param port The port on the host.
055         */
056        public DatagramOutputStream(String host, int port) {
057            this.port = port;
058            try {
059                address = InetAddress.getByName(host);
060            } catch (UnknownHostException ex) {
061                String msg = "Could not find host " + host;
062                LOGGER.error(msg, ex);
063                throw new AppenderRuntimeException(msg, ex);
064            }
065    
066            try {
067                ds = new DatagramSocket();
068            } catch (SocketException ex) {
069                String msg = "Could not instantiate DatagramSocket to " + host;
070                LOGGER.error(msg, ex);
071                throw new AppenderRuntimeException(msg, ex);
072            }
073        }
074    
075        @Override
076        public synchronized void write(byte[] bytes, int offset, int length) throws IOException {
077            copy(bytes, offset, length);
078        }
079    
080        @Override
081        public synchronized void write(int i) throws IOException {
082            copy(new byte[] {(byte) (i >>> SHIFT_3), (byte) (i >>> SHIFT_2), (byte) (i >>> SHIFT_1), (byte) i}, 0, 4);
083        }
084    
085        @Override
086        public synchronized void write(byte[] bytes) throws IOException {
087            copy(bytes, 0, bytes.length);
088        }
089    
090        @Override
091        public synchronized void flush() throws IOException {
092            if (this.ds != null && this.address != null) {
093                DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
094                ds.send(packet);
095            }
096            data = null;
097        }
098    
099        @Override
100        public synchronized void close() throws IOException {
101            if (ds != null) {
102                if (data != null) {
103                    flush();
104                }
105                ds.close();
106                ds = null;
107            }
108        }
109    
110        private void copy(byte[] bytes, int offset, int length) {
111            int index = data == null ? 0 : data.length;
112            byte[] copy = new byte[length + index];
113            if (data != null) {
114                System.arraycopy(data, 0, copy, 0, index);
115            }
116            System.arraycopy(bytes, offset, copy, index, length);
117            data = copy;
118        }
119    }