FELIX-1891 - IO Connector Service implementation - first commit.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@890151 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/io/pom.xml b/io/pom.xml
new file mode 100644
index 0000000..72a176f
--- /dev/null
+++ b/io/pom.xml
@@ -0,0 +1,107 @@
+<!--
+ 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.
+-->
+<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">
+ <parent>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>felix-parent</artifactId>
+ <version>1.2.0</version>
+ <relativePath>../../pom/pom.xml</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <packaging>bundle</packaging>
+ <name>Apache Felix IO Connector Service</name>
+ <version>0.9.0-SNAPSHOT</version>
+ <artifactId>org.apache.felix.io</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>4.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <version>4.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>org.osgi.foundation</artifactId>
+ <version>1.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>Apache Felix IO Connector Service</Bundle-Name>
+ <Bundle-Description>
+ An implementation of the OSGi IO Connector Service
+ </Bundle-Description>
+ <Bundle-Activator>
+ ${pom.artifactId}.Activator </Bundle-Activator>
+ <Bundle-SymbolicName>
+ ${pom.artifactId}
+ </Bundle-SymbolicName>
+ <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
+ <Export-Package>
+ </Export-Package>
+ <Private-Package>
+ org.apache.felix.io
+ </Private-Package>
+ <Export-Service>
+ org.osgi.service.io.ConnectorService
+ </Export-Service>
+ <Include-Resource>
+ </Include-Resource>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>rat-maven-plugin</artifactId>
+ <configuration>
+ <excludeSubProjects>false</excludeSubProjects>
+ <useEclipseDefaultExcludes>true</useEclipseDefaultExcludes>
+ <useMavenDefaultExcludes>true</useMavenDefaultExcludes>
+ <excludes>
+ <param>doc/*</param>
+ <param>maven-eclipse.xml</param>
+ <param>.checkstyle</param>
+ <param>.externalToolBuilders/*</param>
+ </excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/io/src/main/java/org/apache/felix/io/Activator.java b/io/src/main/java/org/apache/felix/io/Activator.java
new file mode 100644
index 0000000..bb670da
--- /dev/null
+++ b/io/src/main/java/org/apache/felix/io/Activator.java
@@ -0,0 +1,63 @@
+/**
+ * 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.felix.io;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.io.ConnectorService;
+
+/**
+ * IO Connector bundle Activator.
+ *
+ * @version $Rev$ $Date$
+ */
+public class Activator implements BundleActivator
+{
+
+ private ServiceRegistration m_registration;
+ private ConnectorServiceImpl m_connectorService;
+
+ /**
+ * Called when IO Connector bundle is started. Creates and registers IO Connector Service.
+ *
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) throws Exception
+ {
+ m_connectorService = new ConnectorServiceImpl(context);
+ context.registerService(ConnectorService.class.getName(), m_connectorService, null);
+ }
+
+ /**
+ * Called when IO Connector bundle is stopped. Stops IO Connector Service and unregisters the service.
+ *
+ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception
+ {
+ if (m_connectorService != null)
+ {
+ m_connectorService.stop();
+ }
+ if (m_registration != null)
+ {
+ m_registration.unregister();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/io/src/main/java/org/apache/felix/io/ConnectorServiceImpl.java b/io/src/main/java/org/apache/felix/io/ConnectorServiceImpl.java
new file mode 100644
index 0000000..a039381
--- /dev/null
+++ b/io/src/main/java/org/apache/felix/io/ConnectorServiceImpl.java
@@ -0,0 +1,342 @@
+/**
+ * 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.felix.io;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.microedition.io.Connection;
+import javax.microedition.io.ConnectionNotFoundException;
+import javax.microedition.io.Connector;
+import javax.microedition.io.InputConnection;
+import javax.microedition.io.OutputConnection;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.io.ConnectionFactory;
+import org.osgi.service.io.ConnectorService;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * <p>
+ * The Connector Service should be called to create and open <code>javax.microedition.io.Connection</code> objects.
+ * </p>
+ *
+ * @see ConnectorService
+ * @version $Rev$ $Date$
+ */
+public class ConnectorServiceImpl implements ConnectorService
+{
+
+ ServiceTracker m_connFactoryTracker;
+
+ /**
+ * Constructs new ConnectorService.
+ *
+ * @param context
+ * bundleContext @see {@link BundleContext}.
+ */
+ public ConnectorServiceImpl(BundleContext context)
+ {
+ this.m_connFactoryTracker = new ServiceTracker(context, ConnectionFactory.class.getName(), null);
+ m_connFactoryTracker.open();
+ }
+
+ /**
+ * Stops ConnectorService. This method closes {@link ConnectionFactory} serviceTracker.
+ *
+ * @see ServiceTracker#close()
+ */
+ public void stop()
+ {
+ m_connFactoryTracker.close();
+ }
+
+ /**
+ * @see org.osgi.service.io.ConnectorService#openDataInputStream(java.lang.String)
+ */
+ public DataInputStream openDataInputStream(String name) throws IOException
+ {
+ Connection connection = open(name, READ, false);
+ if (!(connection instanceof InputConnection))
+ {
+ try
+ {
+ connection.close();
+ } catch (IOException ioex)
+ {
+
+ }
+
+ throw new IOException("Connection doesn't implement InputConnection" + connection.getClass());
+ }
+
+ return ((InputConnection) connection).openDataInputStream();
+ }
+
+ /**
+ * @see org.osgi.service.io.ConnectorService#openDataOutputStream(java.lang.String)
+ */
+ public DataOutputStream openDataOutputStream(String name) throws IOException
+ {
+ Connection connection = open(name, WRITE, false);
+ if (!(connection instanceof OutputConnection))
+ {
+ try
+ {
+ connection.close();
+ } catch (IOException ioex)
+ {
+
+ }
+
+ throw new IOException("Connection doesn't implement OutputConnection" + connection.getClass());
+ }
+
+ return ((OutputConnection) connection).openDataOutputStream();
+ }
+
+ /**
+ * @see org.osgi.service.io.ConnectorService#openInputStream(java.lang.String)
+ */
+ public InputStream openInputStream(String name) throws IOException
+ {
+ Connection connection = open(name, READ, false);
+ if (!(connection instanceof InputConnection))
+ {
+ try
+ {
+ connection.close();
+ } catch (IOException ioex)
+ {
+
+ }
+
+ throw new IOException("Connection doesn't implement InputConnection" + connection.getClass());
+ }
+
+ return ((InputConnection) connection).openInputStream();
+ }
+
+ /**
+ * @see org.osgi.service.io.ConnectorService#openOutputStream(java.lang.String)
+ */
+ public OutputStream openOutputStream(String name) throws IOException
+ {
+ Connection connection = open(name, WRITE, false);
+ if (!(connection instanceof OutputConnection))
+ {
+ try
+ {
+ connection.close();
+ } catch (IOException ioex)
+ {
+
+ }
+
+ throw new IOException("Connection doesn't implement OutputConnection" + connection.getClass());
+ }
+
+ return ((OutputConnection) connection).openOutputStream();
+ }
+
+ /**
+ * @see org.osgi.service.io.ConnectorService#open(String)
+ */
+ public Connection open(String name) throws IOException
+ {
+ return open(name, READ_WRITE, false);
+ }
+
+ /**
+ * @see org.osgi.service.io.ConnectorService#open(String, int)
+ */
+ public Connection open(String name, int mode) throws IOException
+ {
+ return open(name, mode, false);
+ }
+
+ /**
+ * @see org.osgi.service.io.ConnectorService#open(String, int, boolean)
+ */
+ public Connection open(String name, int mode, boolean timeouts) throws IOException
+ {
+ if (name == null)
+ {
+ throw new IllegalArgumentException("URI for the connection can't be null!");
+ }
+
+ // resolving scheme name
+ int index = name.indexOf(":");
+ if (index == -1)
+ {
+ throw new IllegalArgumentException("Can't resolve scheme name");
+ }
+
+ String scheme = name.substring(0, index);
+
+ ConnectionFactory connFactory = resolveConnectionFactory(scheme);
+ Connection connection = null;
+ if (connFactory != null)
+ {
+ connection = connFactory.createConnection(name, mode, timeouts);
+ }
+ // if connection is not provided go to javax.microedition.io.Connector
+ if (connection == null)
+ {
+ try
+ {
+ connection = Connector.open(name, mode, timeouts);
+ } catch (Exception ex)
+ {
+
+ }
+
+ } else
+ {
+ return connection;
+ }
+
+ throw new ConnectionNotFoundException("Failed to create connection " + name);
+ }
+
+ /**
+ * <p>
+ * Resolves {@link ConnectionFactory} based on IO scheme name. If multiple ConnectionFactory services register with
+ * the same scheme, method select the ConnectionFactory with highest value for service.ranking service registration
+ * property or if more than one ConnectionFactory service has the highest value, the ConnectionFactory service with
+ * the lowest service.id is selected.
+ * </p>
+ *
+ * @param scheme
+ * name of IO scheme.
+ * @return {@link ConnectionFactory} which matched provided scheme.
+ */
+ private ConnectionFactory resolveConnectionFactory(String scheme)
+ {
+ ServiceReference[] references = m_connFactoryTracker.getServiceReferences();
+ if (references == null || references.length == 0)
+ {
+ return null;
+ }
+
+ ServiceReference matchingRef = null;
+ for (int i = 0; i < references.length; i++)
+ {
+ if (containsScheme(references[i], scheme))
+ {
+ if (matchingRef != null)
+ {
+ int matchRanking = getServiceRanking(matchingRef);
+ int foundRanking = getServiceRanking(references[i]);
+ if (foundRanking > matchRanking)
+ {
+ matchingRef = references[i];
+ } else if (foundRanking == matchRanking && compareServiceId(references[i], matchingRef))
+ {
+ matchingRef = references[i];
+ }
+ } else
+ {
+ matchingRef = references[i];
+ }
+ }
+ }
+ if (matchingRef != null)
+ {
+ return (ConnectionFactory) m_connFactoryTracker.getService(matchingRef);
+ }
+ return null;
+ }
+
+ /**
+ * Checks if provided {@link ServiceReference} contains in its service properties provided scheme name.
+ *
+ * @param ref
+ * {@link ServiceReference}.
+ * @param scheme
+ * name of IO scheme.
+ * @return true if contains scheme name, false if not.
+ */
+ private boolean containsScheme(ServiceReference ref, String scheme)
+ {
+ Object property = ref.getProperty(ConnectionFactory.IO_SCHEME);
+ if (property != null && property.equals(scheme))
+ {
+ return true;
+ } else
+ {
+ if (property != null && property instanceof String[])
+ {
+ String[] schemes = (String[]) property;
+ for (int index = 0; index < schemes.length; index++)
+ {
+ if (schemes[index].equals(scheme))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Compare service.id of two {@link ServiceReference}'s.
+ *
+ * @param foundRef
+ * {@link ServiceReference}.
+ * @param prevRef
+ * {@link ServiceReference}.
+ * @return true if first reference has lowest service.id than second, false if not.
+ */
+ private boolean compareServiceId(ServiceReference foundRef, ServiceReference prevRef)
+ {
+ Long foundServiceId = (Long) foundRef.getProperty(Constants.SERVICE_ID);
+ Long prevServiceId = (Long) prevRef.getProperty(Constants.SERVICE_ID);
+ if (foundServiceId.longValue() < prevServiceId.longValue())
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Gets service.ranking from provided {@link ServiceReference}.
+ *
+ * @param ref
+ * {@link ServiceReference}.
+ * @return service.ranking property value.
+ */
+ private int getServiceRanking(ServiceReference ref)
+ {
+ Object property = ref.getProperty(Constants.SERVICE_RANKING);
+ if (property == null || !(property instanceof Integer))
+ {
+ return 0;
+ } else
+ {
+ return ((Integer) property).intValue();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/io/src/test/java/org/apache/felix/io/ConnectionFactoryMock.java b/io/src/test/java/org/apache/felix/io/ConnectionFactoryMock.java
new file mode 100644
index 0000000..7632419
--- /dev/null
+++ b/io/src/test/java/org/apache/felix/io/ConnectionFactoryMock.java
@@ -0,0 +1,131 @@
+/**
+ * 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.felix.io;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.microedition.io.Connection;
+import javax.microedition.io.InputConnection;
+import javax.microedition.io.OutputConnection;
+
+import org.osgi.service.io.ConnectionFactory;
+
+/**
+ * {@link ConnectionFactory} implementation for testing purpose. Creates fake connection @see {@link Connection}.
+ *
+ *
+ * @version $Rev$ $Date$
+ */
+public class ConnectionFactoryMock implements ConnectionFactory
+{
+
+ /**
+ * uri name.
+ */
+ private String m_name;
+ /**
+ * access mode.
+ */
+ private int m_mode;
+ /**
+ * connection timeout.
+ */
+ private boolean m_timeout;
+
+ /**
+ * @see org.osgi.service.io.ConnectionFactory#createConnection(String, int, boolean)
+ */
+ public Connection createConnection(String name, int mode, boolean timeout) throws IOException
+ {
+ this.m_mode = mode;
+ this.m_name = name;
+ this.m_timeout = timeout;
+ return new TestConnection();
+ }
+
+ public String getName()
+ {
+ return m_name;
+ }
+
+ public int getMode()
+ {
+ return m_mode;
+ }
+
+ public boolean isTimeout()
+ {
+ return m_timeout;
+ }
+
+ /**
+ * Mock implementation of {@link Connection}, {@link InputConnection}, {@link OutputConnection}.
+ *
+ */
+ private class TestConnection implements Connection, InputConnection, OutputConnection
+ {
+
+ /**
+ * @see javax.microedition.io.Connection#close()
+ */
+ public void close() throws IOException
+ {
+
+ }
+
+ /**
+ * @see javax.microedition.io.InputConnection#openDataInputStream()
+ */
+ public DataInputStream openDataInputStream() throws IOException
+ {
+ return new DataInputStream(new ByteArrayInputStream(new byte[]
+ {}));
+ }
+
+ /**
+ * @see javax.microedition.io.InputConnection#openInputStream()
+ */
+ public InputStream openInputStream() throws IOException
+ {
+ return new ByteArrayInputStream(new byte[]
+ {});
+ }
+
+ /**
+ * @see javax.microedition.io.OutputConnection#openDataOutputStream()
+ */
+ public DataOutputStream openDataOutputStream() throws IOException
+ {
+ return new DataOutputStream(new ByteArrayOutputStream());
+ }
+
+ /**
+ * @see javax.microedition.io.OutputConnection#openOutputStream()
+ */
+ public OutputStream openOutputStream() throws IOException
+ {
+ return new ByteArrayOutputStream();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/io/src/test/java/org/apache/felix/io/ConnectorServiceTest.java b/io/src/test/java/org/apache/felix/io/ConnectorServiceTest.java
new file mode 100644
index 0000000..ee7bb32
--- /dev/null
+++ b/io/src/test/java/org/apache/felix/io/ConnectorServiceTest.java
@@ -0,0 +1,294 @@
+/**
+ * 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.felix.io;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.microedition.io.Connection;
+import javax.microedition.io.ConnectionNotFoundException;
+
+import junit.framework.TestCase;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.io.ConnectionFactory;
+import org.osgi.service.io.ConnectorService;
+
+/**
+ * <tt>ConnectorServiceTest</tt> represents test class for {@link ConnectorService}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ConnectorServiceTest extends TestCase
+{
+
+ private BundleContext m_bundleContext;
+ private ConnectorService m_service;
+ private ServiceListener m_serviceListener;
+ private long m_serviceId;
+
+ private ServiceRegistration registerConnectionFactory(ConnectionFactoryMock connFactory, Dictionary props)
+ {
+ // service reference for ConnectionFactory service
+ ServiceReference reference = (ServiceReference) Mockito.mock(ServiceReference.class);
+ Mockito.when(reference.getProperty(ConnectionFactory.IO_SCHEME)).thenReturn(
+ props.get(ConnectionFactory.IO_SCHEME));
+ Mockito.when(reference.getProperty(Constants.SERVICE_ID)).thenReturn(new Long(++m_serviceId));
+ Mockito.when(reference.getProperty(Constants.SERVICE_RANKING)).thenReturn(props.get(Constants.SERVICE_RANKING));
+ // service registration for ConnectionFactory service
+ ServiceRegistration registration = (ServiceRegistration) Mockito.mock(ServiceRegistration.class);
+ Mockito.when(registration.getReference()).thenReturn(reference);
+ // service event
+ ServiceEvent registeredEvent = (ServiceEvent) Mockito.mock(ServiceEvent.class);
+ Mockito.when(registeredEvent.getServiceReference()).thenReturn(reference);
+ Mockito.when(new Integer(registeredEvent.getType())).thenReturn(new Integer(ServiceEvent.REGISTERED));
+ Mockito.when(m_bundleContext.getService(reference)).thenReturn(connFactory);
+ // sending registration event
+ // service tracker for ConnectionFactory service used by ConnectorService
+ // will be informed about service registration
+ m_serviceListener.serviceChanged(registeredEvent);
+
+ return registration;
+ }
+
+ private ConnectorService getConnectorService()
+ {
+ return m_service;
+ }
+
+ public void setUp()
+ {
+ m_bundleContext = (BundleContext) Mockito.mock(BundleContext.class);
+ ArgumentCaptor argument = ArgumentCaptor.forClass(ServiceListener.class);
+ m_service = new ConnectorServiceImpl(m_bundleContext);
+ try
+ {
+ ((BundleContext) Mockito.verify(m_bundleContext)).addServiceListener((ServiceListener) argument.capture(),
+ Mockito.anyString());
+ } catch (InvalidSyntaxException e)
+ {
+ fail();
+ }
+ // getting from captor serviceListener which listen to ConnectionFactory service
+ m_serviceListener = (ServiceListener) argument.getValue();
+ }
+
+ public void tearDown()
+ {
+
+ }
+
+ /**
+ * Tests all methods provided by {@link ConnectorService}.
+ *
+ * @throws Exception
+ */
+ public void testOpen() throws Exception
+ {
+ ConnectionFactoryMock connFactory = new ConnectionFactoryMock();
+ Dictionary props = new Hashtable();
+ props.put(ConnectionFactory.IO_SCHEME, "file");
+ ServiceRegistration registration = registerConnectionFactory(connFactory, props);
+ ConnectorService service = getConnectorService();
+
+ Connection connection = service.open("file://test.txt");
+ assertEquals("file://test.txt", connFactory.getName());
+ assertEquals(ConnectorService.READ_WRITE, connFactory.getMode());
+ assertEquals(false, connFactory.isTimeout());
+ assertNotNull("checks returned Connection", connection);
+
+ connection = service.open("file://test.txt", ConnectorService.READ);
+ assertEquals("file://test.txt", connFactory.getName());
+ assertEquals(ConnectorService.READ, connFactory.getMode());
+ assertEquals(false, connFactory.isTimeout());
+ assertNotNull("checks returned Connection", connection);
+
+ connection = service.open("file://test.txt", ConnectorService.WRITE);
+ assertEquals("file://test.txt", connFactory.getName());
+ assertEquals(ConnectorService.WRITE, connFactory.getMode());
+ assertEquals(false, connFactory.isTimeout());
+ assertNotNull("checks returned Connection", connection);
+
+ connection = service.open("file://test.txt", ConnectorService.READ, true);
+ assertEquals("file://test.txt", connFactory.getName());
+ assertEquals(ConnectorService.READ, connFactory.getMode());
+ assertEquals(true, connFactory.isTimeout());
+ assertNotNull("checks returned Connection", connection);
+
+ try
+ {
+ connection = service.open("http://test.txt", ConnectorService.READ);
+ fail("Connection shouldn't be created");
+ } catch (ConnectionNotFoundException e)
+ {
+ // "expected"
+ }
+
+ try
+ {
+ service.open("file.txt");
+ fail("Illegal format of uri");
+ } catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ DataInputStream dataInStream = service.openDataInputStream("file://test.txt");
+ assertEquals("file://test.txt", connFactory.getName());
+ assertEquals(ConnectorService.READ, connFactory.getMode());
+ assertEquals(false, connFactory.isTimeout());
+ assertNotNull("checks returned DataInputStream", dataInStream);
+
+ DataOutputStream dataOutStream = service.openDataOutputStream("file://test.txt");
+ assertEquals("file://test.txt", connFactory.getName());
+ assertEquals(ConnectorService.WRITE, connFactory.getMode());
+ assertEquals(false, connFactory.isTimeout());
+ assertNotNull("checks returned DataOutputStream", dataOutStream);
+
+ InputStream inStream = service.openInputStream("file://test.txt");
+ assertEquals("file://test.txt", connFactory.getName());
+ assertEquals(ConnectorService.READ, connFactory.getMode());
+ assertEquals(false, connFactory.isTimeout());
+ assertNotNull("checks returned InputStream", inStream);
+
+ OutputStream outStream = service.openOutputStream("file://test.txt");
+ assertEquals("file://test.txt", connFactory.getName());
+ assertEquals(ConnectorService.WRITE, connFactory.getMode());
+ assertEquals(false, connFactory.isTimeout());
+ assertNotNull("checks returned OutputStream", outStream);
+
+ registration.unregister();
+ }
+
+ /**
+ * Registers two ConnectionFactory services with same IO_SCHEME. One with higher service.ranking Connector Service
+ * should pickup service with highest service.ranking.
+ *
+ * @throws Exception
+ */
+ public void testHighestRanking() throws Exception
+ {
+ ConnectionFactoryMock connFactory = new ConnectionFactoryMock();
+ Dictionary props = new Hashtable();
+ props.put(ConnectionFactory.IO_SCHEME, "file");
+ registerConnectionFactory(connFactory, props);
+
+ ConnectionFactoryMock connFactory2 = new ConnectionFactoryMock();
+ props.put(Constants.SERVICE_RANKING, new Integer(Integer.MAX_VALUE));
+ registerConnectionFactory(connFactory2, props);
+
+ ConnectorService service = getConnectorService();
+
+ Connection connection = service.open("file://test.txt");
+ assertEquals("uri checks for lowest ranking service", null, connFactory.getName());
+ assertEquals("file://test.txt", connFactory2.getName());
+ assertEquals(ConnectorService.READ_WRITE, connFactory2.getMode());
+ assertEquals(false, connFactory2.isTimeout());
+ assertNotNull("Connection should be created", connection);
+ }
+
+ /**
+ * Registers two ConnectionFactory services with same IO_SCHEME. Connector Service should pickup service with lowest
+ * service.id.
+ *
+ * @throws Exception
+ */
+ public void testLowestServiceId() throws Exception
+ {
+ ConnectionFactoryMock connFactory = new ConnectionFactoryMock();
+ Dictionary props = new Hashtable();
+ props.put(ConnectionFactory.IO_SCHEME, "file");
+ ServiceRegistration registration = registerConnectionFactory(connFactory, props);
+
+ ConnectionFactoryMock connFactory2 = new ConnectionFactoryMock();
+ ServiceRegistration registration2 = registerConnectionFactory(connFactory2, props);
+
+ ConnectorService service = getConnectorService();
+
+ Connection connection = service.open("file://test.txt");
+ assertEquals("uri checks for highest service.id", null, connFactory2.getName());
+ assertEquals("uri checks for lowest service.id", "file://test.txt", connFactory.getName());
+ assertEquals(ConnectorService.READ_WRITE, connFactory.getMode());
+ assertEquals(false, connFactory.isTimeout());
+ assertNotNull("checks returned Connection", connection);
+
+ Long serviceId1 = (Long) registration.getReference().getProperty(Constants.SERVICE_ID);
+ Long serviceId2 = (Long) registration2.getReference().getProperty(Constants.SERVICE_ID);
+
+ assertTrue(serviceId1.longValue() < serviceId2.longValue());
+
+ registration.unregister();
+ registration2.unregister();
+ }
+
+ /**
+ * Tests ConnectionFactory service which support 3 different schemes.
+ *
+ * @throws Exception
+ */
+ public void testMultipleScheme() throws Exception
+ {
+ ConnectionFactoryMock connFactory = new ConnectionFactoryMock();
+ Dictionary props = new Hashtable();
+ props.put(ConnectionFactory.IO_SCHEME, new String[]
+ { "file", "http", "sms" });
+ ServiceRegistration registration = registerConnectionFactory(connFactory, props);
+ ConnectorService service = getConnectorService();
+
+ Connection connection = service.open("file://test.txt");
+ assertEquals("file://test.txt", connFactory.getName());
+ assertEquals(ConnectorService.READ_WRITE, connFactory.getMode());
+ assertEquals(false, connFactory.isTimeout());
+ assertNotNull("checks returned connection", connection);
+
+ connection = service.open("http://test.txt", ConnectorService.READ);
+ assertEquals("http://test.txt", connFactory.getName());
+ assertEquals(ConnectorService.READ, connFactory.getMode());
+ assertEquals(false, connFactory.isTimeout());
+ assertNotNull("checks returned connection", connection);
+
+ connection = service.open("sms://test.txt", ConnectorService.READ);
+ assertEquals("sms://test.txt", connFactory.getName());
+ assertEquals(ConnectorService.READ, connFactory.getMode());
+ assertEquals(false, connFactory.isTimeout());
+ assertNotNull("checks returned connection", connection);
+
+ try
+ {
+ connection = service.open("ftp://test.txt", ConnectorService.READ);
+ fail("Connection shouldn't be created");
+ } catch (ConnectionNotFoundException e)
+ {
+ // "expected"
+ }
+
+ registration.unregister();
+ }
+
+}
\ No newline at end of file