001package ball.activation; 002/*- 003 * ########################################################################## 004 * Utilities 005 * $Id: DataSourceDefaultMethods.java 5285 2020-02-05 04:23:21Z ball $ 006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/ball-util/trunk/src/main/java/ball/activation/DataSourceDefaultMethods.java $ 007 * %% 008 * Copyright (C) 2008 - 2020 Allen D. Ball 009 * %% 010 * Licensed under the Apache License, Version 2.0 (the "License"); 011 * you may not use this file except in compliance with the License. 012 * You may obtain a copy of the License at 013 * 014 * http://www.apache.org/licenses/LICENSE-2.0 015 * 016 * Unless required by applicable law or agreed to in writing, software 017 * distributed under the License is distributed on an "AS IS" BASIS, 018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 019 * See the License for the specific language governing permissions and 020 * limitations under the License. 021 * ########################################################################## 022 */ 023import java.io.IOException; 024import java.io.InputStream; 025import java.io.OutputStream; 026import java.lang.reflect.InvocationTargetException; 027import javax.activation.DataSource; 028import org.apache.commons.io.IOUtils; 029 030/** 031 * {@link DataSource} default method implementations. 032 * 033 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 034 * @version $Revision: 5285 $ 035 */ 036public interface DataSourceDefaultMethods extends DataSource { 037 038 /** 039 * {@link.rfc 2045} {@value #CONTENT_TYPE} 040 */ 041 public static final String CONTENT_TYPE = "Content-Type"; 042 043 /** 044 * {@link.rfc 2045} {@value #APPLICATION_OCTET_STREAM} 045 */ 046 public static final String APPLICATION_OCTET_STREAM = 047 "application/octet-stream"; 048 049 /** 050 * {@link.rfc 3023} {@value #APPLICATION_XML} 051 */ 052 public static final String APPLICATION_XML = "application/xml"; 053 054 /** 055 * {@link.rfc 2045} {@value #TEXT_PLAIN} 056 */ 057 public static final String TEXT_PLAIN = "text/plain"; 058 059 /** 060 * {@link.rfc 2045} {@value #TEXT_HTML} 061 */ 062 public static final String TEXT_HTML = "text/html"; 063 064 default void setName(String name) { 065 throw new UnsupportedOperationException(); 066 } 067 068 default void setContentType(String type) { 069 throw new UnsupportedOperationException(); 070 } 071 072 /** 073 * @throws UnsupportedOperationException 074 * Unless overridden by subclass 075 * implementation. 076 */ 077 @Override 078 default InputStream getInputStream() throws IOException { 079 throw new UnsupportedOperationException(); 080 } 081 082 /** 083 * @throws UnsupportedOperationException 084 * Unless overridden by subclass 085 * implementation. 086 */ 087 @Override 088 default OutputStream getOutputStream() throws IOException { 089 throw new UnsupportedOperationException(); 090 } 091 092 /** 093 * Method to clear the {@link DataSource} and discard any input on any 094 * open {@link #getOutputStream()}. 095 * 096 * @throws UnsupportedOperationException 097 * If {@link #getOutputStream()} throws 098 * {@link UnsupportedOperationException}. 099 */ 100 default void clear() { 101 try { 102 getOutputStream().close(); 103 } catch (IOException exception) { 104 throw new IllegalStateException(exception); 105 } 106 } 107 108 /** 109 * Method to get the number of bytes stored in {@link.this} 110 * {@link DataSource}. Default implementation returns {@code -1}. 111 * 112 * @return The number of bytes stored in {@link.this} 113 * {@link DataSource}; {@code -1} if the count is unknown. 114 */ 115 default long length() { return -1; } 116 117 /** 118 * Method to "wrap" the {@link InputStream} returned by 119 * {@link #getInputStream()}into {@link InputStream} instances. 120 * 121 * @param types The {@link InputStream} implementation 122 * {@link Class}es. 123 * 124 * @return The "wrapped" {@link InputStream}. 125 * 126 * @throws IOException If an I/O error occurs. 127 */ 128 default InputStream getInputStream(Class<?>... types) throws IOException { 129 return wrap(getInputStream(), types); 130 } 131 132 /** 133 * Method to "wrap" the {@link OutputStream} returned by 134 * {@link #getOutputStream()}into {@link OutputStream} instances. 135 * 136 * @param types The {@link OutputStream} implementation 137 * {@link Class}es. 138 * 139 * @return The "wrapped" {@link OutputStream}. 140 * 141 * @throws IOException If an I/O error occurs. 142 */ 143 default OutputStream getOutputStream(Class<?>... types) throws IOException { 144 return wrap(getOutputStream(), types); 145 } 146 147 /** 148 * Method to write the contents of this {@link DataSource} to an 149 * {@link OutputStream}. 150 * 151 * @see #getInputStream() 152 * 153 * @param out The target {@link OutputStream}. 154 * 155 * @throws IOException If a problem is encountered opening or 156 * reading the {@link InputStream} or writing 157 * to the {@link OutputStream}. 158 */ 159 default void writeTo(OutputStream out) throws IOException { 160 try (InputStream in = getInputStream()) { 161 IOUtils.copy(in, out); 162 } 163 } 164 165 /** 166 * Method to "wrap" an {@link InputStream} into {@link InputStream} 167 * instances. 168 * 169 * @param in The {@link InputStream}. 170 * @param types The {@link InputStream} implementation 171 * {@link Class}es. 172 * 173 * @return The "wrapped" {@link InputStream}. 174 * 175 * @throws IOException If any of the wrapping streams throw an 176 * {@link IOException}. 177 */ 178 default InputStream wrap(InputStream in, 179 Class<?>... types) throws IOException { 180 try { 181 for (Class<?> type : types) { 182 in = 183 type.asSubclass(InputStream.class) 184 .getConstructor(InputStream.class) 185 .newInstance(in); 186 } 187 } catch (InvocationTargetException exception) { 188 Throwable cause = exception.getCause(); 189 190 if (cause instanceof IOException) { 191 throw (IOException) cause; 192 } else if (cause instanceof RuntimeException) { 193 throw (RuntimeException) cause; 194 } else if (cause instanceof Error) { 195 throw (Error) cause; 196 } else { 197 throw new IllegalArgumentException(exception); 198 } 199 } catch (RuntimeException exception) { 200 throw exception; 201 } catch (Exception exception) { 202 throw new IllegalArgumentException(exception); 203 } 204 205 return in; 206 } 207 208 /** 209 * Method to "wrap" an {@link OutputStream} into {@link OutputStream} 210 * instances. 211 * 212 * @param out The {@link OutputStream}. 213 * @param types The {@link OutputStream} implementation 214 * {@link Class}es. 215 * 216 * @return The "wrapped" {@link OutputStream}. 217 * 218 * @throws IOException If any of the wrapping streams throw an 219 * {@link IOException}. 220 */ 221 default OutputStream wrap(OutputStream out, 222 Class<?>... types) throws IOException { 223 try { 224 for (Class<?> type : types) { 225 out = 226 type.asSubclass(OutputStream.class) 227 .getConstructor(OutputStream.class) 228 .newInstance(out); 229 } 230 } catch (InvocationTargetException exception) { 231 Throwable cause = exception.getCause(); 232 233 if (cause instanceof IOException) { 234 throw (IOException) cause; 235 } else if (cause instanceof RuntimeException) { 236 throw (RuntimeException) cause; 237 } else if (cause instanceof Error) { 238 throw (Error) cause; 239 } else { 240 throw new IllegalArgumentException(exception); 241 } 242 } catch (RuntimeException exception) { 243 throw exception; 244 } catch (Exception exception) { 245 throw new IllegalArgumentException(exception); 246 } 247 248 return out; 249 } 250}