You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by jl...@apache.org on 2022/05/03 12:22:12 UTC

svn commit: r1900504 [2/22] - in /geronimo/specs/trunk: ./ geronimo-activation_2.0_spec/ geronimo-activation_2.0_spec/src/ geronimo-activation_2.0_spec/src/main/ geronimo-activation_2.0_spec/src/main/java/ geronimo-activation_2.0_spec/src/main/java/jak...

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/ActivationDataFlavor.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/ActivationDataFlavor.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/ActivationDataFlavor.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/ActivationDataFlavor.java Tue May  3 12:22:08 2022
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package jakarta.activation;
+
+import java.awt.datatransfer.DataFlavor;
+import java.io.InputStream;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class ActivationDataFlavor extends DataFlavor {
+    private final Class representationClass;
+    private final String mimeType;
+    private String humanPresentableName;
+
+    public ActivationDataFlavor(Class representationClass, String mimeType, String humanPresentableName) {
+        this.representationClass = representationClass;
+        this.mimeType = mimeType;
+        this.humanPresentableName = humanPresentableName;
+    }
+
+    public ActivationDataFlavor(Class representationClass, String humanPresentableName) {
+        this.representationClass = representationClass;
+        this.mimeType = "application/x-java-serialized-object";
+        this.humanPresentableName = humanPresentableName;
+    }
+
+    public ActivationDataFlavor(String mimeType, String humanPresentableName) {
+        this.mimeType = mimeType;
+        this.representationClass = InputStream.class;
+        this.humanPresentableName = humanPresentableName;
+    }
+
+    public String getMimeType() {
+        return mimeType;
+    }
+
+    public Class getRepresentationClass() {
+        return representationClass;
+    }
+
+    public String getHumanPresentableName() {
+        return humanPresentableName;
+    }
+
+    public void setHumanPresentableName(String humanPresentableName) {
+        this.humanPresentableName = humanPresentableName;
+    }
+
+    public boolean equals(ActivationDataFlavor dataFlavor) {
+        return this.isMimeTypeEqual(dataFlavor.getMimeType()) && representationClass == dataFlavor.getRepresentationClass();
+    }
+
+    public boolean isMimeTypeEqual(String mimeType) {
+        try {
+            MimeType thisType = new MimeType(this.mimeType);
+            MimeType thatType = new MimeType(mimeType);
+            return thisType.match(thatType);
+        } catch (MimeTypeParseException e) {
+            return false;
+        }
+    }
+
+    protected String normalizeMimeTypeParameter(String parameterName, String parameterValue) {
+        return parameterName + "=" + parameterValue;
+    }
+
+    protected String normalizeMimeType(String mimeType) {
+        return mimeType;
+    }
+}
\ No newline at end of file

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/CommandInfo.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/CommandInfo.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/CommandInfo.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/CommandInfo.java Tue May  3 12:22:08 2022
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package jakarta.activation;
+
+import java.beans.Beans;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class CommandInfo {
+    private final String commandName;
+    private final String commandClass;
+
+    /**
+     * Constructor for a CommandInfo
+     *
+     * @param commandName  the command name
+     * @param commandClass the name of the command's implementation class
+     */
+    public CommandInfo(String commandName, String commandClass) {
+        this.commandName = commandName;
+        this.commandClass = commandClass;
+    }
+
+    /**
+     * Return the command name.
+     *
+     * @return the command name
+     */
+    public String getCommandName() {
+        return commandName;
+    }
+
+    /**
+     * Return the implementation class name.
+     *
+     * @return the name of the command's implementation class; may be null
+     */
+    public String getCommandClass() {
+        return commandClass;
+    }
+
+    /**
+     * Instantiate and return a command JavaBean.
+     * The bean is created using Beans.instantiate(loader, commandClass).
+     * If the new bean implements CommandObject then its setCommandContext(String, DataHandler)
+     * method is called.
+     * Otherwise if it implements Externalizable and the supplied DataHandler is not null
+     * then its readExternal(ObjectInputStream) method is called with a stream obtained from
+     * DataHandler.getInputStream().
+     *
+     * @param dh a DataHandler that provides the data to be passed to the command
+     * @param loader the ClassLoader to be used to instantiate the command
+     * @return a new command instance
+     * @throws IOException if there was a problem initializing the command
+     * @throws ClassNotFoundException if the command class could not be found
+     */
+    public Object getCommandObject(DataHandler dh, ClassLoader loader) throws IOException, ClassNotFoundException {
+        Object bean = Beans.instantiate(loader, commandClass);
+        if (bean instanceof CommandObject) {
+            ((CommandObject) bean).setCommandContext(commandName, dh);
+        } else if (bean instanceof Externalizable && dh != null) {
+            ((Externalizable) bean).readExternal(new ObjectInputStream(dh.getInputStream()));
+        }
+        return bean;
+    }
+}
\ No newline at end of file

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/CommandMap.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/CommandMap.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/CommandMap.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/CommandMap.java Tue May  3 12:22:08 2022
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package jakarta.activation;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public abstract class CommandMap {
+    private static CommandMap defaultCommandMap = new MailcapCommandMap();
+
+    /**
+     * Return the default CommandMap. If this has not been explictly set
+     * using setDefaultCommandMap() then a MailcapCommandMap is returned.
+     * @return the default CommandMap
+     */
+    public static CommandMap getDefaultCommandMap() {
+        return defaultCommandMap;
+    }
+
+    /**
+     * Set the default CommandMap.
+     *
+     * @param commandMap the new default CommandMap; if null resets to a MailcapCommandMap
+     * @throws SecurityException if the caller does not have "SetFactory" RuntimePermission
+     */
+    public static void setDefaultCommandMap(CommandMap commandMap) {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkSetFactory();
+        }
+        defaultCommandMap = commandMap == null ? new MailcapCommandMap() : commandMap;
+    }
+
+    public CommandMap() {
+    }
+
+    /**
+     * Get the preferred commands for the given
+     * mimetype, as modified by the DataSource.  The
+     * default implementation is just a delegation to
+     * getPreferredCommands(mimeType), but the concrete
+     * implementations can define additional behavior.
+     *
+     * @param mimeType The mimeType name.
+     * @param ds       The datasource providing the type object.
+     *
+     * @return The array of CommandInfo[] objects associated with
+     *         this mimeType/DataSource combo.
+     */
+    public CommandInfo[] getPreferredCommands(String mimeType, DataSource ds) {
+        return getPreferredCommands(mimeType);
+    }
+
+    /**
+     * Get the preferred commands for the given
+     * mimetype.  The concrete implementations define the
+     * actual behaviour.
+     *
+     * @param mimeType The mimeType name.
+     *
+     * @return The array of CommandInfo[] objects associated with
+     *         this mimeType combo.
+     */
+    public abstract CommandInfo[] getPreferredCommands(String mimeType);
+
+
+    /**
+     * Get the entire command set for the given
+     * mimetype, as modified by the DataSource.  The
+     * default implementation is just a delegation to
+     * getAllCommands(mimeType), but the concrete
+     * implementations can define additional behavior.
+     *
+     * @param mimeType The mimeType name.
+     * @param ds       The datasource providing the type object.
+     *
+     * @return The array of CommandInfo[] objects associated with
+     *         this mimeType/DataSource combo.
+     */
+    public CommandInfo[] getAllCommands(String mimeType, DataSource ds) {
+        return getAllCommands(mimeType);
+    }
+
+
+    /**
+     * Get all available commands for the given
+     * mimetype.  The concrete implementations define the
+     * actual behaviour.
+     *
+     * @param mimeType The mimeType name.
+     *
+     * @return The array of CommandInfo[] objects associated with
+     *         this mimeType combo.
+     */
+    public abstract CommandInfo[] getAllCommands(String mimeType);
+
+    /**
+     * Get the default command implementation for a
+     * given mimeType/DataSource combo.
+     *
+     * The default implementation just delegates to
+     * getCommand(mimeType, cmdName).  Subclasses may
+     * provide more specialized behavior.
+     *
+     * @param mimeType The name of the mime type
+     * @param cmdName  The command action we wish to perform.
+     * @param ds       The modifying DataSource.
+     *
+     * @return A CommandInfo object corresponding to the command
+     *         mapping.
+     */
+    public CommandInfo getCommand(String mimeType, String cmdName, DataSource ds) {
+        return getCommand(mimeType, cmdName);
+    }
+
+    /**
+     * Get the default command implementation for a
+     * give mimeType
+     *
+     * @param mimeType The name of the mime type
+     * @param cmdName  The command action we wish to perform.
+     *
+     * @return A CommandInfo object corresponding to the command
+     *         mapping.
+     */
+    public abstract CommandInfo getCommand(String mimeType, String cmdName);
+
+    /**
+     * Locate a DataContentHandler for the given mime
+     * type.  The concrete implementations determine
+     * how this mapping is performed.
+     *
+     * @param mimeType The target MIME type.
+     * @param ds       The DataSource associated with this request.
+     *
+     * @return The DataContentHandler for the MIME type.
+     */
+    public DataContentHandler createDataContentHandler(String mimeType, DataSource ds) {
+        return createDataContentHandler(mimeType);
+    }
+
+    /**
+     * Locate a DataContentHandler for the given mime
+     * type.  The concrete implementations determine
+     * how this mapping is performed.
+     *
+     * @param mimeType The target MIME type.
+     *
+     * @return The DataContentHandler for the MIME type.
+     */
+    public abstract DataContentHandler createDataContentHandler(String mimeType);
+
+    /**
+     * Return all mime types known to this CommandMap, or null if none.
+     *
+     * @return a String array of all mime types known to this CommandMap
+     */
+    public String[] getMimeTypes() {
+        return null;
+    }
+}
\ No newline at end of file

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/CommandObject.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/CommandObject.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/CommandObject.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/CommandObject.java Tue May  3 12:22:08 2022
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package jakarta.activation;
+
+import java.io.IOException;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public interface CommandObject {
+    public void setCommandContext(String verb, DataHandler dh) throws IOException;
+}
\ No newline at end of file

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/DataContentHandler.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/DataContentHandler.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/DataContentHandler.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/DataContentHandler.java Tue May  3 12:22:08 2022
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package jakarta.activation;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public interface DataContentHandler {
+    public ActivationDataFlavor[]  getTransferDataFlavors();
+
+    public Object getTransferData(ActivationDataFlavor df, DataSource ds) throws IOException;
+
+    public Object getContent(DataSource ds) throws IOException;
+
+    public void writeTo(Object obj, String mimeType, OutputStream os) throws IOException;
+}
\ No newline at end of file

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/DataContentHandlerFactory.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/DataContentHandlerFactory.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/DataContentHandlerFactory.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/DataContentHandlerFactory.java Tue May  3 12:22:08 2022
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package jakarta.activation;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public interface DataContentHandlerFactory {
+    public DataContentHandler createDataContentHandler(String mimeType);
+}

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/DataHandler.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/DataHandler.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/DataHandler.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/DataHandler.java Tue May  3 12:22:08 2022
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package jakarta.activation;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.net.URL;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class DataHandler {
+    private final DataSource ds;
+    private final ActivationDataFlavor flavor;
+
+    private CommandMap commandMap;
+    private DataContentHandler dch;
+
+    public DataHandler(DataSource ds) {
+        this.ds = ds;
+        this.flavor = new ActivationDataFlavor(ds.getContentType(), null);
+    }
+
+    public DataHandler(Object data, String type) {
+        this.ds = new ObjectDataSource(data, type);
+        this.flavor = new ActivationDataFlavor(data.getClass(), null);
+    }
+
+    public DataHandler(URL url) {
+        this.ds = new URLDataSource(url);
+        this.flavor = new ActivationDataFlavor(ds.getContentType(), null);
+    }
+
+    public DataSource getDataSource() {
+        return ds;
+    }
+
+    public String getName() {
+        return ds.getName();
+    }
+
+    public String getContentType() {
+        return ds.getContentType();
+    }
+
+    public InputStream getInputStream() throws IOException {
+        return ds.getInputStream();
+    }
+
+    public void writeTo(OutputStream os) throws IOException {
+        if (ds instanceof ObjectDataSource) {
+            ObjectDataSource ods = (ObjectDataSource) ds;
+            DataContentHandler dch = getDataContentHandler();
+            if (dch == null) {
+                throw new UnsupportedDataTypeException(ods.mimeType);
+            }
+            dch.writeTo(ods.data, ods.mimeType, os);
+        } else {
+            byte[] buffer = new byte[1024];
+            InputStream is = getInputStream();
+            try {
+                int count;
+                while ((count = is.read(buffer)) != -1) {
+                    os.write(buffer, 0, count);
+                }
+            } finally {
+                is.close();
+            }
+        }
+    }
+
+    public OutputStream getOutputStream() throws IOException {
+        return ds.getOutputStream();
+    }
+
+    public synchronized ActivationDataFlavor[] getTransferDataFlavors() {
+        return getDataContentHandler().getTransferDataFlavors();
+    }
+
+    public boolean isDataFlavorSupported(ActivationDataFlavor flavor) {
+        final  ActivationDataFlavor[] flavors = getTransferDataFlavors();
+        for (int i = 0; i < flavors.length; i++) {
+            ActivationDataFlavor dataFlavor = flavors[i];
+            if (dataFlavor.equals(flavor)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public Object getTransferData(ActivationDataFlavor flavor) throws IOException {
+        final DataContentHandler dch = getDataContentHandler();
+        if (dch != null) {
+            return dch.getTransferData(flavor, ds);
+        } else if (this.flavor.equals(flavor)) {
+            if (ds instanceof ObjectDataSource) {
+                return ((ObjectDataSource) ds).data;
+            } else {
+                return ds.getInputStream();
+            }
+        } else {
+            throw new IOException("Unsupported DataFlavor: " + flavor);
+        }
+    }
+
+    public CommandInfo[] getPreferredCommands() {
+        return getCommandMap().getPreferredCommands(ds.getContentType());
+    }
+
+    public CommandInfo[] getAllCommands() {
+        return getCommandMap().getAllCommands(ds.getContentType());
+    }
+
+    public CommandInfo getCommand(String cmdName) {
+        return getCommandMap().getCommand(ds.getContentType(), cmdName);
+    }
+
+    public Object getContent() throws IOException {
+        if (ds instanceof ObjectDataSource) {
+            return ((ObjectDataSource) ds).data;
+        } else {
+            DataContentHandler dch = getDataContentHandler();
+            if (dch != null) {
+                return dch.getContent(ds);
+            } else {
+                return ds.getInputStream();
+            }
+        }
+    }
+
+    public Object getBean(CommandInfo cmdinfo) {
+        try {
+            return cmdinfo.getCommandObject(this, this.getClass().getClassLoader());
+        } catch (IOException e) {
+            return null;
+        } catch (ClassNotFoundException e) {
+            return null;
+        }
+    }
+
+    /**
+     * A local implementation of DataSouce used to wrap an Object and mime-type.
+     */
+    private class ObjectDataSource implements DataSource {
+        private final Object data;
+        private final String mimeType;
+
+        public ObjectDataSource(Object data, String mimeType) {
+            this.data = data;
+            this.mimeType = mimeType;
+        }
+
+        public String getName() {
+            return null;
+        }
+
+        public String getContentType() {
+            return mimeType;
+        }
+
+        public InputStream getInputStream() throws IOException {
+            final DataContentHandler dch = getDataContentHandler();
+            if (dch == null) {
+                throw new UnsupportedDataTypeException(mimeType);
+            }
+            final PipedInputStream is = new PipedInputStream();
+            final PipedOutputStream os = new PipedOutputStream(is);
+            Thread thread = new Thread("DataHandler Pipe Pump") {
+                public void run() {
+                    try {
+                        try {
+                            dch.writeTo(data, mimeType, os);
+                        } finally {
+                            os.close();
+                        }
+                    } catch (IOException e) {
+                        // ignore, per spec - doh!
+                    }
+                }
+            };
+            thread.start();
+            return is;
+        }
+
+        public OutputStream getOutputStream() throws IOException {
+            return null;
+        }
+    }
+
+    public synchronized void setCommandMap(CommandMap commandMap) {
+        this.commandMap = commandMap;
+        this.dch = null;
+    }
+
+    private synchronized CommandMap getCommandMap() {
+        return commandMap != null ? commandMap : CommandMap.getDefaultCommandMap();
+    }
+
+    /**
+     * Search for a DataContentHandler for our mime type.
+     * The search is performed by first checking if a global factory has been set using
+     * {@link #setDataContentHandlerFactory(DataContentHandlerFactory)};
+     * if found then it is called to attempt to create a handler.
+     * If this attempt fails, we then call the command map set using {@link #setCommandMap(CommandMap)}
+     * (or if that has not been set, the default map returned by {@link CommandMap#getDefaultCommandMap()})
+     * to create the handler.
+     *
+     * The resulting handler is cached until the global factory is changed.
+     *
+     * @return
+     */
+    private synchronized DataContentHandler getDataContentHandler() {
+        DataContentHandlerFactory localFactory;
+        synchronized (DataHandler.class) {
+            if (factory != originalFactory) {
+                // setDCHF was called - clear our cached copy of the DCH and DCHF
+                dch = null;
+                originalFactory = factory;
+            }
+            localFactory = originalFactory;
+        }
+        if (dch == null) {
+            // get the main mime-type portion of the content.
+            String mimeType = getMimeType(ds.getContentType());
+            if (localFactory != null) {
+                dch = localFactory.createDataContentHandler(mimeType);
+            }
+            if (dch == null) {
+                if (commandMap != null) {
+                    dch = commandMap.createDataContentHandler(mimeType);
+                } else {
+                    dch = CommandMap.getDefaultCommandMap().createDataContentHandler(mimeType);
+                }
+            }
+        }
+        return dch;
+    }
+
+    /**
+     * Retrieve the base MIME type from a content type.  This parses
+     * the type into its base components, stripping off any parameter
+     * information.
+     *
+     * @param contentType
+     *               The content type string.
+     *
+     * @return The MIME type identifier portion of the content type.
+     */
+    private String getMimeType(String contentType) {
+        try {
+            MimeType mimeType = new MimeType(contentType);
+            return mimeType.getBaseType();
+        } catch (MimeTypeParseException e) {
+        }
+        return contentType;
+    }
+
+    /**
+     * This is used to check if the DataContentHandlerFactory has been changed.
+     * This is not specified behaviour but this check is required to make this work like the RI.
+     */
+    private DataContentHandlerFactory originalFactory;
+
+    {
+        synchronized (DataHandler.class) {
+            originalFactory = factory;
+        }
+    }
+
+    private static DataContentHandlerFactory factory;
+
+    /**
+     * Set the DataContentHandlerFactory to use.
+     * If this method has already been called then an Error is raised.
+     *
+     * @param newFactory the new factory
+     * @throws SecurityException if the caller does not have "SetFactory" RuntimePermission
+     */
+    public static synchronized void setDataContentHandlerFactory(DataContentHandlerFactory newFactory) {
+        if (factory != null) {
+            throw new Error("jakarta.activation.DataHandler.setDataContentHandlerFactory has already been defined");
+        }
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkSetFactory();
+        }
+        factory = newFactory;
+    }
+}

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/DataSource.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/DataSource.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/DataSource.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/DataSource.java Tue May  3 12:22:08 2022
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package jakarta.activation;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public interface DataSource {
+    public abstract InputStream getInputStream() throws IOException;
+
+    public abstract OutputStream getOutputStream() throws IOException;
+
+    public abstract String getContentType();
+
+    public abstract String getName();
+}

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/FileDataSource.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/FileDataSource.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/FileDataSource.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/FileDataSource.java Tue May  3 12:22:08 2022
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package jakarta.activation;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class FileDataSource implements DataSource {
+    private final File file;
+    private FileTypeMap fileTypeMap;
+
+    /**
+     * Creates a FileDataSource from a File object
+     */
+    public FileDataSource(File file) {
+        this.file = file;
+    }
+
+    /**
+     * Creates a FileDataSource from the specified path name
+     */
+    public FileDataSource(String name) {
+        this(new File(name));
+    }
+
+    /**
+     * Return the InputStream obtained from the data source
+     */
+    public InputStream getInputStream() throws IOException {
+        return new FileInputStream(file);
+    }
+
+    /**
+     * Return the OutputStream obtained from the data source
+     */
+    public OutputStream getOutputStream() throws IOException {
+        return new FileOutputStream(file);
+    }
+
+    /**
+     * Returns the content type of the data source
+     */
+    public String getContentType() {
+        if (fileTypeMap == null) {
+            return FileTypeMap.getDefaultFileTypeMap().getContentType(file);
+        } else {
+            return fileTypeMap.getContentType(file);
+        }
+    }
+
+    /**
+     * Returns the name of the data source object
+     */
+    public String getName() {
+        return file.getName();
+    }
+
+    /**
+     * Returns the data source file
+     */
+    public File getFile() {
+        return file;
+    }
+
+    /**
+     * Sets the FileTypeMap associated with the data source
+     */
+    public void setFileTypeMap(FileTypeMap map) {
+        fileTypeMap = map;
+    }
+}
\ No newline at end of file

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/FileTypeMap.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/FileTypeMap.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/FileTypeMap.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/FileTypeMap.java Tue May  3 12:22:08 2022
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package jakarta.activation;
+
+import java.io.File;
+
+/**
+ * FileTypeMap is an abstract class that provides a data type interface for files.
+ *
+ * @version $Rev$ $Date$
+ */
+public abstract class FileTypeMap {
+    // we use null here rather than a default because
+    // the constructor for MimetypesFileTypeMap does a lot of I/O
+    private static FileTypeMap defaultFileTypeMap = null;
+
+    /**
+     * Sets the default FileTypeMap for the system.
+     * @param fileMap the new default FileTypeMap
+     * @throws SecurityException if the caller does not have "SetFactory" RuntimePermission
+     */
+    public static void setDefaultFileTypeMap(FileTypeMap fileMap) {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkSetFactory();
+        }
+        defaultFileTypeMap = fileMap;
+    }
+
+    /**
+     * Returns the default FileTypeMap
+     * @return the default FileTYpeMap; if null returns a MimetypesFileTypeMap
+     */
+    public synchronized static FileTypeMap getDefaultFileTypeMap() {
+        if (defaultFileTypeMap == null) {
+            defaultFileTypeMap = new MimetypesFileTypeMap();
+        }
+        return defaultFileTypeMap;
+    }
+
+    public FileTypeMap() {
+    }
+
+    public abstract String getContentType(File file);
+
+    public abstract String getContentType(String filename);
+}
\ No newline at end of file

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MailcapCommandMap.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MailcapCommandMap.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MailcapCommandMap.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MailcapCommandMap.java Tue May  3 12:22:08 2022
@@ -0,0 +1,508 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package jakarta.activation;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URL;
+import java.security.Security;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.geronimo.osgi.locator.ProviderLocator;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class MailcapCommandMap extends CommandMap {
+    private final Map mimeTypes = new HashMap();
+    private final Map preferredCommands = new HashMap();
+    private final Map allCommands = new HashMap();
+    // the unparsed commands from the mailcap file.
+    private final Map nativeCommands = new HashMap();
+    // commands identified as fallbacks...these are used last, and also used as wildcards.
+    private final Map fallbackCommands = new HashMap();
+    private URL url;
+
+    public MailcapCommandMap() {
+        ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+        // process /META-INF/mailcap.default
+        try {
+            InputStream is = MailcapCommandMap.class.getResourceAsStream("/META-INF/mailcap.default");
+            if (is != null) {
+                try {
+                    parseMailcap(is);
+                } finally {
+                    is.close();
+                }
+            }
+        } catch (IOException e) {
+            // ignore
+        }
+
+        // process /META-INF/mailcap resources
+        try {
+            Enumeration e = contextLoader.getResources("META-INF/mailcap");
+            while (e.hasMoreElements()) {
+                url = ((URL) e.nextElement());
+                try {
+                    InputStream is = url.openStream();
+                    try {
+                        parseMailcap(is);
+                    } finally {
+                        is.close();
+                    }
+                } catch (IOException e1) {
+                    continue;
+                }
+            }
+        } catch (SecurityException e) {
+            // ignore
+        } catch (IOException e) {
+            // ignore
+        }
+
+        // process ${java.home}/lib/mailcap
+        try {
+            File file = new File(System.getProperty("java.home"), "lib/mailcap");
+            InputStream is = new FileInputStream(file);
+            try {
+                parseMailcap(is);
+            } finally {
+                is.close();
+            }
+        } catch (SecurityException e) {
+            // ignore
+        } catch (IOException e) {
+            // ignore
+        }
+
+        // process ${user.home}/lib/mailcap
+        try {
+            File file = new File(System.getProperty("user.home"), ".mailcap");
+            InputStream is = new FileInputStream(file);
+            try {
+                parseMailcap(is);
+            } finally {
+                is.close();
+            }
+        } catch (SecurityException e) {
+            // ignore
+        } catch (IOException e) {
+            // ignore
+        }
+    }
+
+    public MailcapCommandMap(String fileName) throws IOException {
+        this();
+        FileReader reader = new FileReader(fileName);
+        try {
+            parseMailcap(reader);
+        } finally {
+            reader.close();
+        }
+    }
+
+    public MailcapCommandMap(InputStream is) {
+        this();
+        parseMailcap(is);
+    }
+
+    private void parseMailcap(InputStream is) {
+        try {
+            parseMailcap(new InputStreamReader(is));
+        } catch (IOException e) {
+            // spec API means all we can do is swallow this
+        }
+    }
+
+    void parseMailcap(Reader reader) throws IOException {
+        BufferedReader br = new BufferedReader(reader);
+        String line;
+        while ((line = br.readLine()) != null) {
+            addMailcap(line);
+        }
+    }
+
+    public synchronized void addMailcap(String mail_cap) {
+        int index = 0;
+        // skip leading whitespace
+        index = skipSpace(mail_cap, index);
+        if (index == mail_cap.length() || mail_cap.charAt(index) == '#') {
+            return;
+        }
+
+        // get primary type
+        int start = index;
+        index = getToken(mail_cap, index);
+        if (start == index) {
+            return;
+        }
+        String mimeType = mail_cap.substring(start, index);
+
+        // skip any spaces after the primary type
+        index = skipSpace(mail_cap, index);
+        if (index == mail_cap.length() || mail_cap.charAt(index) == '#') {
+            return;
+        }
+
+        // get sub-type
+        if (mail_cap.charAt(index) == '/') {
+            index = skipSpace(mail_cap, ++index);
+            start = index;
+            index = getToken(mail_cap, index);
+            mimeType = mimeType + '/' + mail_cap.substring(start, index);
+        } else {
+
+            mimeType = mimeType + "/*";
+        }
+
+        // we record all mappings using the lowercase version.
+        mimeType = mimeType.toLowerCase();
+
+        // skip spaces after mime type
+        index = skipSpace(mail_cap, index);
+
+        // expect a ';' to terminate field 1
+        if (index == mail_cap.length() || mail_cap.charAt(index) != ';') {
+            return;
+        }
+        // ok, we've parsed the mime text field, now parse the view field.  If there's something
+        // there, then we add this to the native text.
+        index = skipSpace(mail_cap, index + 1);
+        // if the next encountered text is not a ";", then we have a view.  This gets added to the
+        // native list.
+        if (index == mail_cap.length() || mail_cap.charAt(index) != ';') {
+            ArrayList nativeCommandList = (ArrayList)nativeCommands.get(mimeType);
+
+            // if this is the first for this mimetype, create a holder
+            if (nativeCommandList == null) {
+                nativeCommandList = new ArrayList();
+                nativeCommands.put(mimeType, nativeCommandList);
+            }
+
+            // now add this as an entry in the list.
+            nativeCommandList.add(mail_cap);
+            // now skip forward to the next field marker, if any
+            index = getMText(mail_cap, index);
+        }
+
+        // we don't know which list this will be added to until we finish parsing, as there
+        // can be an x-java-fallback-entry parameter that moves this to the fallback list.
+        List commandList = new ArrayList();
+        // but by default, this is not a fallback.
+        boolean fallback = false;
+
+        int fieldNumber = 0;
+
+        // parse fields
+        while (index < mail_cap.length() && mail_cap.charAt(index) == ';') {
+            index = skipSpace(mail_cap, index + 1);
+            start = index;
+            index = getToken(mail_cap, index);
+            String fieldName = mail_cap.substring(start, index).toLowerCase();
+            index = skipSpace(mail_cap, index);
+            if (index < mail_cap.length() && mail_cap.charAt(index) == '=') {
+                index = skipSpace(mail_cap, index + 1);
+                start = index;
+                index = getMText(mail_cap, index);
+                String value = mail_cap.substring(start, index);
+                index = skipSpace(mail_cap, index);
+                if (fieldName.startsWith("x-java-") && fieldName.length() > 7) {
+                    String command = fieldName.substring(7);
+                    value = value.trim();
+                    if (command.equals("fallback-entry")) {
+                        if (value.equals("true")) {
+                            fallback = true;
+                        }
+                    }
+                    else {
+                        // create a CommandInfo item and add it the accumulator
+                        CommandInfo info = new CommandInfo(command, value);
+                        commandList.add(info);
+                    }
+                }
+            }
+        }
+        addCommands(mimeType, commandList, fallback);
+    }
+
+    /**
+     * Add a parsed list of commands to the appropriate command list.
+     *
+     * @param mimeType The mimeType name this is added under.
+     * @param commands A List containing the command information.
+     * @param fallback The target list identifier.
+     */
+    private void addCommands(String mimeType, List commands, boolean fallback) {
+        // add this to the mimeType set
+        mimeTypes.put(mimeType, mimeType);
+        // the target list changes based on the type of entry.
+        Map target = fallback ? fallbackCommands : preferredCommands;
+
+        // now process
+        for (Iterator i = commands.iterator(); i.hasNext();) {
+            CommandInfo info = (CommandInfo)i.next();
+            addCommand(target, mimeType, info);
+            // if this is not a fallback position, then this to the allcommands list.
+            if (!fallback) {
+                List cmdList = (List) allCommands.get(mimeType);
+                if (cmdList == null) {
+                    cmdList = new ArrayList();
+                    allCommands.put(mimeType, cmdList);
+                }
+                cmdList.add(info);
+            }
+        }
+    }
+
+
+    /**
+     * Add a command to a target command list (preferred or fallback).
+     *
+     * @param commandList
+     *                 The target command list.
+     * @param mimeType The MIME type the command is associated with.
+     * @param command  The command information.
+     */
+    private void addCommand(Map commandList, String mimeType, CommandInfo command) {
+
+        Map commands = (Map) commandList.get(mimeType);
+        if (commands == null) {
+            commands = new HashMap();
+            commandList.put(mimeType, commands);
+        }
+        commands.put(command.getCommandName(), command);
+    }
+
+
+    private int skipSpace(String s, int index) {
+        while (index < s.length() && Character.isWhitespace(s.charAt(index))) {
+            index++;
+        }
+        return index;
+    }
+
+    private int getToken(String s, int index) {
+        while (index < s.length() && s.charAt(index) != '#' && !MimeType.isSpecial(s.charAt(index))) {
+            index++;
+        }
+        return index;
+    }
+
+    private int getMText(String s, int index) {
+        while (index < s.length()) {
+            char c = s.charAt(index);
+            if (c == '#' || c == ';' || Character.isISOControl(c)) {
+                return index;
+            }
+            if (c == '\\') {
+                index++;
+                if (index == s.length()) {
+                    return index;
+                }
+            }
+            index++;
+        }
+        return index;
+    }
+
+    public synchronized CommandInfo[] getPreferredCommands(String mimeType) {
+        // get the mimetype as a lowercase version.
+        mimeType = mimeType.toLowerCase();
+
+        Map commands = (Map) preferredCommands.get(mimeType);
+        if (commands == null) {
+            commands = (Map) preferredCommands.get(getWildcardMimeType(mimeType));
+        }
+
+        Map fallbackCommands = getFallbackCommands(mimeType);
+
+        // if we have fall backs, then we need to merge this stuff.
+        if (fallbackCommands != null) {
+            // if there's no command list, we can just use this as the master list.
+            if (commands == null) {
+                commands = fallbackCommands;
+            }
+            else {
+                // merge the two lists.  The ones in the commands list will take precedence.
+                commands = mergeCommandMaps(commands, fallbackCommands);
+            }
+        }
+
+        // now convert this into an array result.
+        if (commands == null) {
+            return new CommandInfo[0];
+        }
+        return (CommandInfo[]) commands.values().toArray(new CommandInfo[commands.size()]);
+    }
+
+    private Map getFallbackCommands(String mimeType) {
+        Map commands = (Map) fallbackCommands.get(mimeType);
+
+        // now we also need to search this as if it was a wildcard.  If we get a wildcard hit,
+        // we have to merge the two lists.
+        Map wildcardCommands = (Map)fallbackCommands.get(getWildcardMimeType(mimeType));
+        // no wildcard version
+        if (wildcardCommands == null) {
+            return commands;
+        }
+        // we need to merge these.
+        return mergeCommandMaps(commands, wildcardCommands);
+    }
+
+
+    private Map mergeCommandMaps(Map main, Map fallback) {
+        // create a cloned copy of the second map.  We're going to use a PutAll operation to
+        // overwrite any duplicates.
+        Map result = new HashMap(fallback);
+        result.putAll(main);
+
+        return result;
+    }
+
+    public synchronized CommandInfo[] getAllCommands(String mimeType) {
+        mimeType = mimeType.toLowerCase();
+        List exactCommands = (List) allCommands.get(mimeType);
+        if (exactCommands == null) {
+            exactCommands = Collections.EMPTY_LIST;
+        }
+        List wildCommands = (List) allCommands.get(getWildcardMimeType(mimeType));
+        if (wildCommands == null) {
+            wildCommands = Collections.EMPTY_LIST;
+        }
+
+        Map fallbackCommands = getFallbackCommands(mimeType);
+        if (fallbackCommands == null) {
+            fallbackCommands = Collections.EMPTY_MAP;
+        }
+
+
+        CommandInfo[] result = new CommandInfo[exactCommands.size() + wildCommands.size() + fallbackCommands.size()];
+        int j = 0;
+        for (int i = 0; i < exactCommands.size(); i++) {
+            result[j++] = (CommandInfo) exactCommands.get(i);
+        }
+        for (int i = 0; i < wildCommands.size(); i++) {
+            result[j++] = (CommandInfo) wildCommands.get(i);
+        }
+
+        for (Iterator i = fallbackCommands.keySet().iterator(); i.hasNext();) {
+            result[j++] = (CommandInfo) fallbackCommands.get((String)i.next());
+        }
+        return result;
+    }
+
+    public synchronized CommandInfo getCommand(String mimeType, String cmdName) {
+        mimeType = mimeType.toLowerCase();
+        // strip any parameters from the supplied mimeType
+        int i = mimeType.indexOf(';');
+        if (i != -1) {
+            mimeType = mimeType.substring(0, i).trim();
+        }
+
+        // search for an exact match
+        Map commands = (Map) preferredCommands.get(mimeType);
+        if (commands == null) {
+            // then a wild card match
+            commands = (Map) preferredCommands.get(getWildcardMimeType(mimeType));
+            if (commands == null) {
+                // then fallback searches, both standard and wild card.
+                commands = (Map) fallbackCommands.get(mimeType);
+                if (commands == null) {
+                    commands = (Map) fallbackCommands.get(getWildcardMimeType(mimeType));
+                }
+                if (commands == null) {
+                    return null;
+                }
+            }
+        }
+        return (CommandInfo) commands.get(cmdName.toLowerCase());
+    }
+
+    private String getWildcardMimeType(String mimeType) {
+        int i = mimeType.indexOf('/');
+        if (i == -1) {
+            return mimeType + "/*";
+        } else {
+            return mimeType.substring(0, i + 1) + "*";
+        }
+    }
+
+    public synchronized DataContentHandler createDataContentHandler(String mimeType) {
+
+        CommandInfo info = getCommand(mimeType, "content-handler");
+        if (info == null) {
+            return null;
+        }
+
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        if (cl == null) {
+            cl = getClass().getClassLoader();
+        }
+        try {
+            return (DataContentHandler) ProviderLocator.loadClass(info.getCommandClass(), this.getClass(), cl).newInstance();
+        } catch (ClassNotFoundException e) {
+            return null;
+        } catch (IllegalAccessException e) {
+            return null;
+        } catch (InstantiationException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Get all MIME types known to this command map.
+     *
+     * @return A String array of the MIME type names.
+     */
+    public synchronized String[] getMimeTypes() {
+        ArrayList types = new ArrayList(mimeTypes.values());
+        return (String[])types.toArray(new String[types.size()]);
+    }
+
+    /**
+     * Return the list of raw command strings parsed
+     * from the mailcap files for a given mimeType.
+     *
+     * @param mimeType The target mime type
+     *
+     * @return A String array of the raw command strings.  Returns
+     *         an empty array if the mimetype is not currently known.
+     */
+    public synchronized String[] getNativeCommands(String mimeType) {
+        ArrayList commands = (ArrayList)nativeCommands.get(mimeType.toLowerCase());
+        if (commands == null) {
+            return new String[0];
+        }
+        return (String[])commands.toArray(new String[commands.size()]);
+    }
+}

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MimeType.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MimeType.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MimeType.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MimeType.java Tue May  3 12:22:08 2022
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package jakarta.activation;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class MimeType implements Externalizable {
+    private static final String SPECIALS = "()<>@,;:\\\"/[]?=";
+
+    static boolean isSpecial(char c) {
+        return Character.isWhitespace(c) || Character.isISOControl(c) || SPECIALS.indexOf(c) != -1;
+    }
+
+    private String primaryType = "application";
+    private String subType = "*";
+    private final MimeTypeParameterList parameterList = new MimeTypeParameterList();;
+
+    public MimeType() {
+    }
+
+    public MimeType(String rawdata) throws MimeTypeParseException {
+        parseMimeType(rawdata);
+    }
+
+    public MimeType(String primary, String sub) throws MimeTypeParseException {
+        setPrimaryType(primary);
+        setSubType(sub);
+    }
+
+    public String getPrimaryType() {
+        return primaryType;
+    }
+
+    public void setPrimaryType(String primary) throws MimeTypeParseException {
+        primaryType = parseToken(primary);
+    }
+
+    public String getSubType() {
+        return subType;
+    }
+
+    public void setSubType(String sub) throws MimeTypeParseException {
+        subType = parseToken(sub);
+    }
+
+    public MimeTypeParameterList getParameters() {
+        return parameterList;
+    }
+
+    public String getParameter(String name) {
+        return parameterList.get(name);
+    }
+
+    public void setParameter(String name, String value) {
+        parameterList.set(name, value);
+    }
+
+    public void removeParameter(String name) {
+        parameterList.remove(name);
+    }
+
+    public String toString() {
+        return getBaseType() + parameterList.toString();
+    }
+
+    public String getBaseType() {
+        return getPrimaryType() + '/' + getSubType();
+    }
+
+    public boolean match(MimeType type) {
+        if (!primaryType.equals(type.primaryType)) return false;
+        if ("*".equals(subType)) return true;
+        if ("*".equals(type.subType)) return true;
+        return subType.equals(type.subType);
+    }
+
+    public boolean match(String rawdata) throws MimeTypeParseException {
+        return match(new MimeType(rawdata));
+    }
+
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeUTF(toString());
+        out.flush();
+    }
+
+    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        try {
+            parseMimeType(in.readUTF());
+        } catch (MimeTypeParseException mtpex) {
+            throw new IOException(mtpex.getMessage());
+        }
+    }
+
+    private void parseMimeType(String rawData) throws MimeTypeParseException {
+        int index = rawData.indexOf('/');
+        if (index == -1) {
+            throw new MimeTypeParseException("Expected '/'");
+        }
+        setPrimaryType(rawData.substring(0, index));
+        int index2 = rawData.indexOf(';', index+1);
+        if (index2 == -1) {
+            setSubType(rawData.substring(index+1));
+        } else {
+            setSubType(rawData.substring(index+1, index2));
+            parameterList.parse(rawData.substring(index2));
+        }
+    }
+
+    private static String parseToken(String tokenString) throws MimeTypeParseException {
+        tokenString = tokenString.trim();
+        for (int i=0; i < tokenString.length(); i++) {
+            char c = tokenString.charAt(i);
+            if (isSpecial(c)) {
+                throw new MimeTypeParseException("Special '" + c + "' not allowed in token");
+            }
+        }
+        return tokenString;
+    }
+}

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MimeTypeParameterList.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MimeTypeParameterList.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MimeTypeParameterList.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MimeTypeParameterList.java Tue May  3 12:22:08 2022
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package jakarta.activation;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class MimeTypeParameterList {
+
+    private final Map params = new HashMap();
+
+    public MimeTypeParameterList() {
+    }
+
+    public MimeTypeParameterList(String parameterList) throws MimeTypeParseException {
+        parse(parameterList);
+    }
+
+    protected void parse(String parameterList) throws MimeTypeParseException {
+        if (parameterList == null) {
+            throw new MimeTypeParseException("parameterList is null");
+        }
+
+        RFC2045Parser parser = new RFC2045Parser(parameterList);
+        while (parser.hasMoreParams()) {
+            String attribute = parser.expectAttribute();
+            parser.expectEquals();
+            String value = parser.expectValue();
+            params.put(attribute.toLowerCase(), value);
+        }
+    }
+
+    public int size() {
+        return params.size();
+    }
+
+    public boolean isEmpty() {
+        return params.isEmpty();
+    }
+
+    public String get(String name) {
+        return (String) params.get(name.toLowerCase());
+    }
+
+    public void set(String name, String value) {
+        params.put(name.toLowerCase(), value);
+    }
+
+    public void remove(String name) {
+        params.remove(name.toLowerCase());
+    }
+
+    public Enumeration getNames() {
+        return Collections.enumeration(params.keySet());
+    }
+
+    /**
+     * String representation of this parameter list.
+     *
+     * @return
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer(params.size() << 4);
+        for (Iterator i = params.entrySet().iterator(); i.hasNext();) {
+            Map.Entry entry = (Map.Entry) i.next();
+            buf.append("; ").append(entry.getKey()).append('=');
+            quote(buf, (String) entry.getValue());
+        }
+        return buf.toString();
+    }
+
+    private void quote(StringBuffer buf, String value) {
+        int length = value.length();
+        boolean quote = false;
+        for (int i = 0; i < length; i++) {
+            if (MimeType.isSpecial(value.charAt(i))) {
+                quote = true;
+                break;
+            }
+        }
+        if (quote) {
+            buf.append('"');
+            for (int i = 0; i < length; i++) {
+                char c = value.charAt(i);
+                if (c == '\\' || c == '"') {
+                    buf.append('\\');
+                }
+                buf.append(c);
+            }
+            buf.append('"');
+        } else {
+            buf.append(value);
+        }
+    }
+
+    private static class RFC2045Parser {
+        private final String text;
+        private int index = 0;
+
+        private RFC2045Parser(String text) {
+            this.text = text;
+        }
+
+        /**
+         * Look the next ";" to start a parameter (skipping whitespace)
+         *
+         * @return
+         */
+        private boolean hasMoreParams() throws MimeTypeParseException {
+            char c;
+            do {
+                if (index == text.length()) {
+                    return false;
+                }
+                c = text.charAt(index++);
+            } while (Character.isWhitespace(c));
+            if (c != ';') {
+                throw new MimeTypeParseException("Expected \";\" at " + (index - 1) + " in " + text);
+            }
+            return true;
+        }
+
+        private String expectAttribute() throws MimeTypeParseException {
+            char c;
+            do {
+                if (index == text.length()) {
+                    throw new MimeTypeParseException("Expected attribute at " + (index - 1) + " in " + text);
+                }
+                c = text.charAt(index++);
+            } while (Character.isWhitespace(c));
+            int start = index - 1;
+            while (index != text.length() && !MimeType.isSpecial(text.charAt(index))) {
+                index += 1;
+            }
+            return text.substring(start, index);
+        }
+
+        private void expectEquals() throws MimeTypeParseException {
+            char c;
+            do {
+                if (index == text.length()) {
+                    throw new MimeTypeParseException("Expected \"=\" at " + (index - 1) + " in " + text);
+                }
+                c = text.charAt(index++);
+            } while (Character.isWhitespace(c));
+            if (c != '=') {
+                throw new MimeTypeParseException("Expected \"=\" at " + (index - 1) + " in " + text);
+            }
+        }
+
+        private String expectValue() throws MimeTypeParseException {
+            char c;
+            do {
+                if (index == text.length()) {
+                    throw new MimeTypeParseException("Expected value at " + (index - 1) + " in " + text);
+                }
+                c = text.charAt(index++);
+            } while (Character.isWhitespace(c));
+            if (c == '"') {
+                // quoted-string
+                StringBuffer buf = new StringBuffer();
+                while (true) {
+                    if (index == text.length()) {
+                        throw new MimeTypeParseException("Expected closing quote at " + (index - 1) + " in " + text);
+                    }
+                    c = text.charAt(index++);
+                    if (c == '"') {
+                        break;
+                    }
+                    if (c == '\\') {
+                        if (index == text.length()) {
+                            throw new MimeTypeParseException("Expected escaped char at " + (index - 1) + " in " + text);
+                        }
+                        c = text.charAt(index++);
+                    }
+                    buf.append(c);
+                }
+                return buf.toString();
+            } else {
+                // token
+                int start = index - 1;
+                while (index != text.length() && !MimeType.isSpecial(text.charAt(index))) {
+                    index += 1;
+                }
+                return text.substring(start, index);
+            }
+        }
+    }
+}
\ No newline at end of file

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MimeTypeParseException.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MimeTypeParseException.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MimeTypeParseException.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MimeTypeParseException.java Tue May  3 12:22:08 2022
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package jakarta.activation;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class MimeTypeParseException extends Exception {
+    public MimeTypeParseException() {
+        super();
+    }
+
+    public MimeTypeParseException(String s) {
+        super(s);
+    }
+}

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MimetypesFileTypeMap.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MimetypesFileTypeMap.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MimetypesFileTypeMap.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/MimetypesFileTypeMap.java Tue May  3 12:22:08 2022
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package jakarta.activation;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.FileReader;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+import java.util.Enumeration;
+import java.net.URL;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class MimetypesFileTypeMap extends FileTypeMap {
+    private static final String DEFAULT_TYPE = "application/octet-stream";
+
+    private final Map types = new HashMap();
+
+    public MimetypesFileTypeMap() {
+        // defaults from /META-INF/mimetypes.default
+        try {
+            InputStream is = MimetypesFileTypeMap.class.getResourceAsStream("/META-INF/mimetypes.default");
+            if (is != null) {
+                try {
+                    loadStream(is);
+                } finally {
+                    is.close();
+                }
+            }
+        } catch (IOException e) {
+            // ignore
+        }
+
+        // defaults from resources called /META-INF/mime.types
+        try {
+            ClassLoader cl = MimetypesFileTypeMap.class.getClassLoader();
+            if (cl == null) {
+                cl = ClassLoader.getSystemClassLoader();
+            }
+            Enumeration e = cl.getResources("/META-INF/mime.types");
+            while (e.hasMoreElements()) {
+                URL url = (URL) e.nextElement();
+                try {
+                    InputStream is = url.openStream();
+                    try {
+                        loadStream(is);
+                    } finally {
+                        is.close();
+                    }
+                } catch (IOException e1) {
+                    continue;
+                }
+            }
+        } catch (SecurityException e) {
+            // ignore
+        } catch (IOException e) {
+            // ignore
+        }
+
+        // defaults from ${java.home}/lib/mime.types
+        try {
+            File file = new File(System.getProperty("java.home"), "lib/mime.types");
+            InputStream is = new FileInputStream(file);
+            try {
+                loadStream(is);
+            } finally {
+                is.close();
+            }
+        } catch (SecurityException e) {
+            // ignore
+        } catch (FileNotFoundException e) {
+            // ignore
+        } catch (IOException e) {
+            // ignore
+        }
+
+        // defaults from ${user.home}/.mime.types
+        try {
+            File file = new File(System.getProperty("user.home"), ".mime.types");
+            InputStream is = new FileInputStream(file);
+            try {
+                loadStream(is);
+            } finally {
+                is.close();
+            }
+        } catch (SecurityException e) {
+            // ignore
+        } catch (FileNotFoundException e) {
+            // ignore
+        } catch (IOException e) {
+            // ignore
+        }
+    }
+
+    public MimetypesFileTypeMap(String mimeTypeFileName) throws IOException {
+        this();
+        BufferedReader reader = new BufferedReader(new FileReader(mimeTypeFileName));
+        try {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                addMimeTypes(line);
+            }
+            reader.close();
+        } catch (IOException e) {
+            try {
+                reader.close();
+            } catch (IOException e1) {
+                // ignore to allow original cause through
+            }
+            throw e;
+        }
+    }
+
+    public MimetypesFileTypeMap(InputStream is) {
+        this();
+        try {
+            loadStream(is);
+        } catch (IOException e) {
+            // ignore as the spec's signature says we can't throw IOException - doh!
+        }
+    }
+
+    private void loadStream(InputStream is) throws IOException {
+        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+        String line;
+        while ((line = reader.readLine()) != null) {
+            addMimeTypes(line);
+        }
+    }
+
+    public synchronized void addMimeTypes(String mime_types) {
+        int hashPos = mime_types.indexOf('#');
+        if (hashPos != -1) {
+            mime_types = mime_types.substring(0, hashPos);
+        }
+        StringTokenizer tok = new StringTokenizer(mime_types);
+        if (!tok.hasMoreTokens()) {
+            return;
+        }
+        String contentType = tok.nextToken();
+        while (tok.hasMoreTokens()) {
+            String fileType = tok.nextToken();
+            types.put(fileType, contentType);
+        }
+    }
+
+    public String getContentType(File f) {
+        return getContentType(f.getName());
+    }
+
+    public synchronized String getContentType(String filename) {
+        int index = filename.lastIndexOf('.');
+        if (index == -1 || index == filename.length()-1) {
+            return DEFAULT_TYPE;
+        }
+        String fileType = filename.substring(index + 1);
+        String contentType = (String) types.get(fileType);
+        return contentType == null ? DEFAULT_TYPE : contentType;
+    }
+}

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/URLDataSource.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/URLDataSource.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/URLDataSource.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/URLDataSource.java Tue May  3 12:22:08 2022
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package jakarta.activation;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class URLDataSource implements DataSource {
+    private final static String DEFAULT_CONTENT_TYPE = "application/octet-stream";
+
+    private final URL url;
+
+    /**
+     * Creates a URLDataSource from a URL object.
+     */
+    public URLDataSource(URL url) {
+        this.url = url;
+    }
+
+    /**
+     * Returns the value of the URL content-type header field.
+     * This method calls URL.openConnection() to obtain a connection
+     * from which to obtain the content type. If this fails or
+     * a getContentType() returns null then "application/octet-stream"
+     * is returned.
+     */
+    public String getContentType() {
+        try {
+            URLConnection connection = url.openConnection();
+            String type = connection.getContentType();
+            return type == null ? DEFAULT_CONTENT_TYPE : type;
+        } catch (IOException e) {
+            return DEFAULT_CONTENT_TYPE;
+        }
+    }
+
+    /**
+     * Returns the file name of the URL object.
+     * @return the name as returned by URL.getFile()
+     */
+    public String getName() {
+        return url.getFile();
+    }
+
+    /**
+     * Returns an InputStream obtained from the URL.
+     * @return the InputStream from URL.openStream()
+     */
+    public InputStream getInputStream() throws IOException {
+        return url.openStream();
+    }
+
+    /**
+     * Returns an OutputStream obtained from the URL.
+     */
+    public OutputStream getOutputStream() throws IOException {
+        URLConnection connection = url.openConnection();
+        connection.setDoOutput(true);
+        return connection.getOutputStream();
+    }
+
+    /**
+     * Returns the URL of the data source.
+     */
+    public URL getURL() {
+        return url;
+    }
+}
\ No newline at end of file

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/UnsupportedDataTypeException.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/UnsupportedDataTypeException.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/UnsupportedDataTypeException.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/jakarta/activation/UnsupportedDataTypeException.java Tue May  3 12:22:08 2022
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package jakarta.activation;
+
+import java.io.IOException;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class UnsupportedDataTypeException extends IOException {
+    public UnsupportedDataTypeException() {
+        super();
+    }
+
+    public UnsupportedDataTypeException(String s) {
+        super(s);
+    }
+}

Added: geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/org/apache/geronimo/specs/activation/Activator.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/org/apache/geronimo/specs/activation/Activator.java?rev=1900504&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/org/apache/geronimo/specs/activation/Activator.java (added)
+++ geronimo/specs/trunk/geronimo-activation_2.0_spec/src/main/java/org/apache/geronimo/specs/activation/Activator.java Tue May  3 12:22:08 2022
@@ -0,0 +1,104 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.geronimo.specs.activation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.LogService;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+/**
+ * The activator that starts and manages the tracking of
+ * JAF activation command maps
+ */
+public class Activator extends org.apache.geronimo.osgi.locator.Activator {
+    // tracker to watch for bundle updates
+    protected BundleTracker bt;
+    // service tracker for a logging service
+    protected ServiceTracker lst;
+    // an array of all active logging services.
+    List<LogService> logServices = new ArrayList<LogService>();
+
+
+    @Override
+    public synchronized void start(final BundleContext context) throws Exception {
+        super.start(context);
+        lst = new LogServiceTracker(context, LogService.class.getName(), null);
+        lst.open();
+	    bt = new BundleTracker(context, Bundle.ACTIVE, new CommandMapBundleTrackerCustomizer(this, context.getBundle()));
+	    bt.open();
+	}
+
+    @Override
+	public synchronized void stop(BundleContext context) throws Exception {
+	    bt.close();
+	    lst.close();
+        super.stop(context);
+	}
+
+	void log(int level, String message) {
+	    synchronized (logServices) {
+	        for (LogService log : logServices) {
+	            log.log(level, message);
+	        }
+        }
+	}
+
+	void log(int level, String message, Throwable th) {
+        synchronized (logServices) {
+            for (LogService log : logServices) {
+                log.log(level, message, th);
+            }
+        }
+    }
+
+	private final class LogServiceTracker extends ServiceTracker {
+        private LogServiceTracker(BundleContext context, String clazz,
+                ServiceTrackerCustomizer customizer) {
+            super(context, clazz, customizer);
+        }
+
+        @Override
+        public Object addingService(ServiceReference reference) {
+            Object svc = super.addingService(reference);
+            if (svc instanceof LogService) {
+                synchronized (logServices) {
+                    logServices.add((LogService) svc);
+                }
+            }
+            return svc;
+        }
+
+        @Override
+        public void removedService(ServiceReference reference, Object service) {
+            synchronized (logServices) {
+                logServices.remove(service);
+            }
+            super.removedService(reference, service);
+        }
+    }
+}