[SM-4760] Add activation-api 2.0.1 bundle
diff --git a/activation-api-2.0.1/pom.xml b/activation-api-2.0.1/pom.xml
new file mode 100644
index 0000000..0094028
--- /dev/null
+++ b/activation-api-2.0.1/pom.xml
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>specs-pom</artifactId>
+ <version>2</version>
+ <relativePath>../specs-pom/pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.activation-api-2.0.1</artifactId>
+ <packaging>bundle</packaging>
+ <version>2.0.1_1-SNAPSHOT</version>
+ <name>Apache ServiceMix :: Specs :: Activation API 2.0.1</name>
+
+ <scm>
+ <connection>scm:git:https://gitbox.apache.org/repos/asf/servicemix-specs.git</connection>
+ <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/servicemix-specs.git</developerConnection>
+ <url>https://gitbox.apache.org/repos/asf?p=servicemix-specs.git</url>
+ </scm>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.locator</artifactId>
+ <version>2.10</version>
+ </dependency>
+ <dependency>
+ <groupId>jakarta.activation</groupId>
+ <artifactId>jakarta.activation-api</artifactId>
+ <version>2.0.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>1.4.0</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <inherited>true</inherited>
+ <configuration>
+ <compilerArgument>-Xbootclasspath/p:${settings.localRepository}/org/apache/geronimo/specs/geronimo-activation_1.1_spec/1.1/geronimo-activation_1.1_spec-1.1.jar</compilerArgument>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Description>${project.description}</Bundle-Description>
+ <Export-Package>
+ javax.activation*;version=2.0.1;-split-package:=merge-first;-noimport:=true,
+ jakarta.activation*;version=2.0.1;-split-package:=merge-first;-noimport:=true
+ </Export-Package>
+ <Import-Package>
+ *,
+ !org.apache.servicemix.specs.activation
+ </Import-Package>
+ <Private-Package>
+ org.apache.servicemix.specs.locator;-split-package:=merge-first,
+ org.apache.servicemix.specs.activation;-split-package:=merge-first
+ </Private-Package>
+ <Bundle-Activator>org.apache.servicemix.specs.activation.Activator</Bundle-Activator>
+ <Implementation-Title>Apache ServiceMix</Implementation-Title>
+ <Implementation-Version>${project.version}</Implementation-Version>
+ </instructions>
+ <unpackBundle>true</unpackBundle>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <artifactSet>
+ <includes>
+ <include>jakarta.activation:jakarta.activation-api</include>
+ <include>org.apache.servicemix.specs:org.apache.servicemix.specs.locator</include>
+ <include>${project.groupId}:${project.artifactId}</include>
+ </includes>
+ </artifactSet>
+ <filters>
+ <filter>
+ <artifact>jakarta.activation:jakarta.activation-api</artifact>
+ <excludes>
+ <exclude>javax/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.servicemix.specs:org.apache.servicemix.specs.locator</artifact>
+ <excludes>
+ <exclude>org/**</exclude>
+ </excludes>
+ </filter>
+ </filters>
+ <createSourcesJar>${createSourcesJar}</createSourcesJar>
+ <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
+ <createDependencyReducedPom>true</createDependencyReducedPom>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>deploy</id>
+ <properties>
+ <createSourcesJar>true</createSourcesJar>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <phase>process-classes</phase>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-sources</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.locator</artifactId>
+ <classifier>sources</classifier>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${project.build.directory}/sources</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>package</id>
+ <phase>package</phase>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <minmemory>128m</minmemory>
+ <maxmemory>512m</maxmemory>
+ <sourcepath>${project.build.directory}/sources</sourcepath>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+</project>
diff --git a/activation-api-2.0.1/src/main/java/javax/activation/DataHandler.java b/activation-api-2.0.1/src/main/java/javax/activation/DataHandler.java
new file mode 100644
index 0000000..b4b4bcf
--- /dev/null
+++ b/activation-api-2.0.1/src/main/java/javax/activation/DataHandler.java
@@ -0,0 +1,305 @@
+/*
+ * 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 javax.activation;
+
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.net.URL;
+
+public class DataHandler implements Transferable {
+ private final DataSource ds;
+ private final DataFlavor 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 DataFlavor[] getTransferDataFlavors() {
+ return getDataContentHandler().getTransferDataFlavors();
+ }
+
+ public boolean isDataFlavorSupported(DataFlavor flavor) {
+ DataFlavor[] flavors = getTransferDataFlavors();
+ for (int i = 0; i < flavors.length; i++) {
+ DataFlavor dataFlavor = flavors[i];
+ if (dataFlavor.equals(flavor)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
+ DataContentHandler dch = getDataContentHandler();
+ if (dch != null) {
+ return dch.getTransferData(flavor, ds);
+ } else if (this.flavor.match(flavor)) {
+ if (ds instanceof ObjectDataSource) {
+ return ((ObjectDataSource) ds).data;
+ } else {
+ return ds.getInputStream();
+ }
+ } else {
+ throw new UnsupportedFlavorException(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) {
+ dch = getCommandMap().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("javax.activation.DataHandler.setDataContentHandlerFactory has already been defined");
+ }
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkSetFactory();
+ }
+ factory = newFactory;
+ }
+}
diff --git a/activation-api-2.0.1/src/main/java/javax/activation/MailcapCommandMap.java b/activation-api-2.0.1/src/main/java/javax/activation/MailcapCommandMap.java
new file mode 100644
index 0000000..536083f
--- /dev/null
+++ b/activation-api-2.0.1/src/main/java/javax/activation/MailcapCommandMap.java
@@ -0,0 +1,515 @@
+/*
+ * 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 javax.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;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class MailcapCommandMap extends CommandMap {
+ /**
+ * A string that holds all the special chars.
+ */
+ private static final String TSPECIALS = "()<>@,;:/[]?=\\\"";
+ 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.
+ */
+ protected 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.
+ */
+ protected 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) != '#' && !isSpecialCharacter(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();
+ }
+ cmdName = cmdName.toLowerCase();
+
+ // search for an exact match
+ Map commands = (Map) preferredCommands.get(mimeType);
+ if (commands == null || commands.get(cmdName) == null) {
+ // then a wild card match
+ commands = (Map) preferredCommands.get(getWildcardMimeType(mimeType));
+ if (commands == null || commands.get(cmdName) == null) {
+ // then fallback searches, both standard and wild card.
+ commands = (Map) fallbackCommands.get(mimeType);
+ if (commands == null || commands.get(cmdName) == null) {
+ commands = (Map) fallbackCommands.get(getWildcardMimeType(mimeType));
+ }
+ if (commands == null) {
+ return null;
+ }
+ }
+ }
+ return (CommandInfo) commands.get(cmdName);
+ }
+
+ 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) cl.loadClass(info.getCommandClass()).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()]);
+ }
+
+ private boolean isSpecialCharacter(char c) {
+ return Character.isWhitespace(c) || Character.isISOControl(c) || TSPECIALS.indexOf(c) != -1;
+ }
+}
diff --git a/activation-api-2.0.1/src/main/java/javax/activation/MimetypesFileTypeMap.java b/activation-api-2.0.1/src/main/java/javax/activation/MimetypesFileTypeMap.java
new file mode 100644
index 0000000..bf8836a
--- /dev/null
+++ b/activation-api-2.0.1/src/main/java/javax/activation/MimetypesFileTypeMap.java
@@ -0,0 +1,203 @@
+/*
+ * 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 javax.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.io.StringReader;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+import java.util.Enumeration;
+import java.net.URL;
+
+/**
+ * @version $Rev: 467742 $ $Date: 2006-10-25 21:30:38 +0200 (Wed, 25 Oct 2006) $
+ */
+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) {
+ 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) {
+ addMimeTypesOneLine(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) {
+ addMimeTypesOneLine(line);
+ }
+ }
+
+ private synchronized void addMimeTypesOneLine(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 synchronized void addMimeTypes(String mime_types) {
+ BufferedReader reader = new BufferedReader(new StringReader(mime_types));
+ try {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ addMimeTypesOneLine(line);
+ }
+ reader.close();
+ } catch (IOException e) {
+ try {
+ reader.close();
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }
+
+
+ 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;
+ }
+}
diff --git a/activation-api-2.0.1/src/main/java/org/apache/servicemix/specs/activation/Activator.java b/activation-api-2.0.1/src/main/java/org/apache/servicemix/specs/activation/Activator.java
new file mode 100644
index 0000000..fc2643f
--- /dev/null
+++ b/activation-api-2.0.1/src/main/java/org/apache/servicemix/specs/activation/Activator.java
@@ -0,0 +1,128 @@
+/*
+ * 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.servicemix.specs.activation;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.activation.CommandMap;
+import javax.activation.DataContentHandler;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * A bundle activator for activation framework
+ */
+public class Activator extends org.apache.servicemix.specs.locator.Activator {
+
+ private Map<Long, MailCap> mailcaps = new ConcurrentHashMap<Long, MailCap>();
+
+ @Override
+ public void start(BundleContext bundleContext) throws Exception {
+ super.start(bundleContext);
+ }
+
+ @Override
+ public void stop(BundleContext bundleContext) throws Exception {
+ super.stop(bundleContext);
+ CommandMap.setDefaultCommandMap(null);
+ }
+
+ @Override
+ protected void register(Bundle bundle) {
+ debugPrintln("checking bundle " + bundle.getBundleId());
+ URL url = bundle.getResource("/META-INF/mailcap");
+ if (url != null) {
+ debugPrintln("found mailcap at " + url);
+
+ try {
+ final Class<?> clazz = bundle
+ .loadClass("javax.activation.DataContentHandler");
+ if (!clazz.isAssignableFrom(DataContentHandler.class)) {
+ debugPrintln("incompatible DataContentHandler class in bundle "
+ + bundle.getBundleId());
+ return;
+ }
+ } catch (ClassNotFoundException e) {
+ // ignored
+ }
+
+ try {
+ mailcaps.put(bundle.getBundleId(), new MailCap(bundle, url));
+ } catch (IOException ex) {
+ // ignored
+ }
+ rebuildCommandMap();
+ }
+ }
+
+ @Override
+ protected void unregister(long bundleId) {
+ MailCap mailcap = mailcaps.remove(bundleId);
+ if (mailcap != null ){
+ debugPrintln("removing mailcap for bundle " + mailcap.bundle.getBundleId());
+ rebuildCommandMap();
+ }
+ }
+
+ private void rebuildCommandMap() {
+ ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+ OsgiMailcapCommandMap commandMap = new OsgiMailcapCommandMap();
+ for (MailCap mailcap : mailcaps.values()) {
+ for (String line : mailcap.lines) {
+ commandMap.addMailcap(line, mailcap.bundle);
+ }
+ }
+ CommandMap.setDefaultCommandMap(commandMap);
+ } finally {
+ Thread.currentThread().setContextClassLoader(tccl);
+ }
+ }
+
+ private static class MailCap {
+ Bundle bundle;
+ List<String> lines;
+
+ private MailCap(Bundle bundle, URL url) throws IOException {
+ this.bundle = bundle;
+ this.lines = new ArrayList<String>();
+ InputStream is = url.openStream();
+ try {
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
+ String line;
+ while ((line = br.readLine()) != null) {
+ lines.add(line);
+ }
+ } finally {
+ is.close();
+ }
+ }
+ }
+
+}
diff --git a/activation-api-2.0.1/src/main/java/org/apache/servicemix/specs/activation/OsgiMailcapCommandMap.java b/activation-api-2.0.1/src/main/java/org/apache/servicemix/specs/activation/OsgiMailcapCommandMap.java
new file mode 100644
index 0000000..a914247
--- /dev/null
+++ b/activation-api-2.0.1/src/main/java/org/apache/servicemix/specs/activation/OsgiMailcapCommandMap.java
@@ -0,0 +1,84 @@
+/*
+ * 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.servicemix.specs.activation;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import javax.activation.MailcapCommandMap;
+import javax.activation.CommandInfo;
+import javax.activation.DataContentHandler;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * An OSGi enabled MailcapCommandMap
+ */
+public class OsgiMailcapCommandMap extends MailcapCommandMap {
+
+ private Bundle currentBundle;
+ private Map<CommandInfo, Bundle> bundles = new HashMap<CommandInfo, Bundle>();
+
+ public void addMailcap(String line, Bundle bundle) {
+ currentBundle = bundle;
+ addMailcap(line);
+ }
+
+ protected void addCommand(Map commandList, String mimeType, CommandInfo command) {
+ super.addCommand(commandList, mimeType, command);
+ if (currentBundle != null) {
+ bundles.put(command, currentBundle);
+ }
+ }
+
+ @Override
+ public DataContentHandler createDataContentHandler(String mimeType) {
+ CommandInfo info = getCommand(mimeType, "content-handler");
+ if (info == null) {
+ return null;
+ }
+
+ Bundle bundle = bundles.get(info);
+ if (bundle != null) {
+ try {
+ return (DataContentHandler) bundle.loadClass(info.getCommandClass()).newInstance();
+ } catch (ClassNotFoundException e) {
+ return null;
+ } catch (IllegalAccessException e) {
+ return null;
+ } catch (InstantiationException e) {
+ return null;
+ }
+ }
+
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ if (cl == null) {
+ cl = getClass().getClassLoader();
+ }
+ try {
+ return (DataContentHandler) cl.loadClass(info.getCommandClass()).newInstance();
+ } catch (ClassNotFoundException e) {
+ return null;
+ } catch (IllegalAccessException e) {
+ return null;
+ } catch (InstantiationException e) {
+ return null;
+ }
+ }
+}