View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.net;
18  
19  import java.io.IOException;
20  import java.io.OutputStream;
21  import java.net.DatagramPacket;
22  import java.net.DatagramSocket;
23  import java.net.InetAddress;
24  import java.net.SocketException;
25  import java.net.UnknownHostException;
26  
27  import org.apache.logging.log4j.Logger;
28  import org.apache.logging.log4j.core.appender.AppenderLoggingException;
29  import org.apache.logging.log4j.status.StatusLogger;
30  
31  /**
32   * OutputStream for UDP connections.
33   */
34  public class DatagramOutputStream extends OutputStream {
35  
36      /**
37       * Allow subclasses access to the status logger without creating another instance.
38       */
39      protected static final Logger LOGGER = StatusLogger.getLogger();
40  
41      private static final int SHIFT_1 = 8;
42      private static final int SHIFT_2 = 16;
43      private static final int SHIFT_3 = 24;
44  
45      private DatagramSocket ds;
46      private final InetAddress address;
47      private final int port;
48  
49      private byte[] data;
50  
51      private final byte[] header;
52      private final byte[] footer;
53  
54      /**
55       * The Constructor.
56       * @param host The host to connect to.
57       * @param port The port on the host.
58       */
59      public DatagramOutputStream(final String host, final int port, final byte[] header, final byte[] footer) {
60          this.port = port;
61          this.header = header;
62          this.footer = footer;
63          try {
64              address = InetAddress.getByName(host);
65          } catch (final UnknownHostException ex) {
66              final String msg = "Could not find host " + host;
67              LOGGER.error(msg, ex);
68              throw new AppenderLoggingException(msg, ex);
69          }
70  
71          try {
72              ds = new DatagramSocket();
73          } catch (final SocketException ex) {
74              final String msg = "Could not instantiate DatagramSocket to " + host;
75              LOGGER.error(msg, ex);
76              throw new AppenderLoggingException(msg, ex);
77          }
78      }
79  
80      @Override
81      public synchronized void write(final byte[] bytes, final int offset, final int length) throws IOException {
82          copy(bytes, offset, length);
83      }
84  
85      @Override
86      public synchronized void write(final int i) throws IOException {
87          copy(new byte[] {(byte) (i >>> SHIFT_3), (byte) (i >>> SHIFT_2), (byte) (i >>> SHIFT_1), (byte) i}, 0, 4);
88      }
89  
90      @Override
91      public synchronized void write(final byte[] bytes) throws IOException {
92          copy(bytes, 0, bytes.length);
93      }
94  
95      @Override
96      public synchronized void flush() throws IOException {
97          try {
98              if (this.data != null && this.ds != null && this.address != null) {
99                  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 }