/** * 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(); } } }