001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020 package javax.activation; 021 022 import java.awt.datatransfer.DataFlavor; 023 import java.awt.datatransfer.Transferable; 024 import java.awt.datatransfer.UnsupportedFlavorException; 025 import java.io.IOException; 026 import java.io.InputStream; 027 import java.io.OutputStream; 028 import java.io.PipedInputStream; 029 import java.io.PipedOutputStream; 030 import java.net.URL; 031 032 public class DataHandler implements Transferable { 033 private final DataSource ds; 034 private final DataFlavor flavor; 035 036 private CommandMap commandMap; 037 private DataContentHandler dch; 038 039 public DataHandler(DataSource ds) { 040 this.ds = ds; 041 this.flavor = new ActivationDataFlavor(ds.getContentType(), null); 042 } 043 044 public DataHandler(Object data, String type) { 045 this.ds = new ObjectDataSource(data, type); 046 this.flavor = new ActivationDataFlavor(data.getClass(), null); 047 } 048 049 public DataHandler(URL url) { 050 this.ds = new URLDataSource(url); 051 this.flavor = new ActivationDataFlavor(ds.getContentType(), null); 052 } 053 054 public DataSource getDataSource() { 055 return ds; 056 } 057 058 public String getName() { 059 return ds.getName(); 060 } 061 062 public String getContentType() { 063 return ds.getContentType(); 064 } 065 066 public InputStream getInputStream() throws IOException { 067 return ds.getInputStream(); 068 } 069 070 public void writeTo(OutputStream os) throws IOException { 071 if (ds instanceof ObjectDataSource) { 072 ObjectDataSource ods = (ObjectDataSource) ds; 073 DataContentHandler dch = getDataContentHandler(); 074 if (dch == null) { 075 throw new UnsupportedDataTypeException(ods.mimeType); 076 } 077 dch.writeTo(ods.data, ods.mimeType, os); 078 } else { 079 byte[] buffer = new byte[1024]; 080 InputStream is = getInputStream(); 081 try { 082 int count; 083 while ((count = is.read(buffer)) != -1) { 084 os.write(buffer, 0, count); 085 } 086 } finally { 087 is.close(); 088 } 089 } 090 } 091 092 public OutputStream getOutputStream() throws IOException { 093 return ds.getOutputStream(); 094 } 095 096 public synchronized DataFlavor[] getTransferDataFlavors() { 097 return getDataContentHandler().getTransferDataFlavors(); 098 } 099 100 public boolean isDataFlavorSupported(DataFlavor flavor) { 101 DataFlavor[] flavors = getTransferDataFlavors(); 102 for (int i = 0; i < flavors.length; i++) { 103 DataFlavor dataFlavor = flavors[i]; 104 if (dataFlavor.equals(flavor)) { 105 return true; 106 } 107 } 108 return false; 109 } 110 111 public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { 112 DataContentHandler dch = getDataContentHandler(); 113 if (dch != null) { 114 return dch.getTransferData(flavor, ds); 115 } else if (this.flavor.match(flavor)) { 116 if (ds instanceof ObjectDataSource) { 117 return ((ObjectDataSource) ds).data; 118 } else { 119 return ds.getInputStream(); 120 } 121 } else { 122 throw new UnsupportedFlavorException(flavor); 123 } 124 } 125 126 public CommandInfo[] getPreferredCommands() { 127 return getCommandMap().getPreferredCommands(ds.getContentType()); 128 } 129 130 public CommandInfo[] getAllCommands() { 131 return getCommandMap().getAllCommands(ds.getContentType()); 132 } 133 134 public CommandInfo getCommand(String cmdName) { 135 return getCommandMap().getCommand(ds.getContentType(), cmdName); 136 } 137 138 public Object getContent() throws IOException { 139 if (ds instanceof ObjectDataSource) { 140 return ((ObjectDataSource) ds).data; 141 } else { 142 DataContentHandler dch = getDataContentHandler(); 143 if (dch != null) { 144 return dch.getContent(ds); 145 } else { 146 return ds.getInputStream(); 147 } 148 } 149 } 150 151 public Object getBean(CommandInfo cmdinfo) { 152 try { 153 return cmdinfo.getCommandObject(this, this.getClass().getClassLoader()); 154 } catch (IOException e) { 155 return null; 156 } catch (ClassNotFoundException e) { 157 return null; 158 } 159 } 160 161 /** 162 * A local implementation of DataSouce used to wrap an Object and mime-type. 163 */ 164 private class ObjectDataSource implements DataSource { 165 private final Object data; 166 private final String mimeType; 167 168 public ObjectDataSource(Object data, String mimeType) { 169 this.data = data; 170 this.mimeType = mimeType; 171 } 172 173 public String getName() { 174 return null; 175 } 176 177 public String getContentType() { 178 return mimeType; 179 } 180 181 public InputStream getInputStream() throws IOException { 182 final DataContentHandler dch = getDataContentHandler(); 183 if (dch == null) { 184 throw new UnsupportedDataTypeException(mimeType); 185 } 186 final PipedInputStream is = new PipedInputStream(); 187 final PipedOutputStream os = new PipedOutputStream(is); 188 Thread thread = new Thread("DataHandler Pipe Pump") { 189 public void run() { 190 try { 191 try { 192 dch.writeTo(data, mimeType, os); 193 } finally { 194 os.close(); 195 } 196 } catch (IOException e) { 197 // ignore, per spec - doh! 198 } 199 } 200 }; 201 thread.start(); 202 return is; 203 } 204 205 public OutputStream getOutputStream() throws IOException { 206 return null; 207 } 208 } 209 210 public synchronized void setCommandMap(CommandMap commandMap) { 211 this.commandMap = commandMap; 212 this.dch = null; 213 } 214 215 private synchronized CommandMap getCommandMap() { 216 return commandMap != null ? commandMap : CommandMap.getDefaultCommandMap(); 217 } 218 219 /** 220 * Search for a DataContentHandler for our mime type. 221 * The search is performed by first checking if a global factory has been set using 222 * {@link #setDataContentHandlerFactory(DataContentHandlerFactory)}; 223 * if found then it is called to attempt to create a handler. 224 * If this attempt fails, we then call the command map set using {@link #setCommandMap(CommandMap)} 225 * (or if that has not been set, the default map returned by {@link CommandMap#getDefaultCommandMap()}) 226 * to create the handler. 227 * 228 * The resulting handler is cached until the global factory is changed. 229 * 230 * @return 231 */ 232 private synchronized DataContentHandler getDataContentHandler() { 233 DataContentHandlerFactory localFactory; 234 synchronized (DataHandler.class) { 235 if (factory != originalFactory) { 236 // setDCHF was called - clear our cached copy of the DCH and DCHF 237 dch = null; 238 originalFactory = factory; 239 } 240 localFactory = originalFactory; 241 } 242 if (dch == null) { 243 // get the main mime-type portion of the content. 244 String mimeType = getMimeType(ds.getContentType()); 245 if (localFactory != null) { 246 dch = localFactory.createDataContentHandler(mimeType); 247 } 248 if (dch == null) { 249 dch = CommandMap.getDefaultCommandMap().createDataContentHandler(mimeType); 250 } 251 } 252 return dch; 253 } 254 255 /** 256 * Retrieve the base MIME type from a content type. This parses 257 * the type into its base components, stripping off any parameter 258 * information. 259 * 260 * @param contentType 261 * The content type string. 262 * 263 * @return The MIME type identifier portion of the content type. 264 */ 265 private String getMimeType(String contentType) { 266 try { 267 MimeType mimeType = new MimeType(contentType); 268 return mimeType.getBaseType(); 269 } catch (MimeTypeParseException e) { 270 } 271 return contentType; 272 } 273 274 /** 275 * This is used to check if the DataContentHandlerFactory has been changed. 276 * This is not specified behaviour but this check is required to make this work like the RI. 277 */ 278 private DataContentHandlerFactory originalFactory; 279 280 { 281 synchronized (DataHandler.class) { 282 originalFactory = factory; 283 } 284 } 285 286 private static DataContentHandlerFactory factory; 287 288 /** 289 * Set the DataContentHandlerFactory to use. 290 * If this method has already been called then an Error is raised. 291 * 292 * @param newFactory the new factory 293 * @throws SecurityException if the caller does not have "SetFactory" RuntimePermission 294 */ 295 public static synchronized void setDataContentHandlerFactory(DataContentHandlerFactory newFactory) { 296 if (factory != null) { 297 throw new Error("javax.activation.DataHandler.setDataContentHandlerFactory has already been defined"); 298 } 299 SecurityManager sm = System.getSecurityManager(); 300 if (sm != null) { 301 sm.checkSetFactory(); 302 } 303 factory = newFactory; 304 } 305 }