cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dav...@apache.org
Subject svn commit: r1353598 - in /cxf/dosgi/trunk: discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/ discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/ dsw/cxf-dsw/src/main/jav...
Date Mon, 25 Jun 2012 15:40:44 GMT
Author: davidb
Date: Mon Jun 25 15:40:42 2012
New Revision: 1353598

URL: http://svn.apache.org/viewvc?rev=1353598&view=rev
Log:
Add ZooKeeper Discovery Plugin to allow transformation of information stored in the ZooKeeper
Discovery system.

Added:
    cxf/dosgi/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/DiscoveryPlugin.java
Modified:
    cxf/dosgi/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/EndpointListenerImpl.java
    cxf/dosgi/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/EndpointListenerImplTest.java
    cxf/dosgi/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ClientServiceFactory.java

Added: cxf/dosgi/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/DiscoveryPlugin.java
URL: http://svn.apache.org/viewvc/cxf/dosgi/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/DiscoveryPlugin.java?rev=1353598&view=auto
==============================================================================
--- cxf/dosgi/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/DiscoveryPlugin.java
(added)
+++ cxf/dosgi/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/DiscoveryPlugin.java
Mon Jun 25 15:40:42 2012
@@ -0,0 +1,52 @@
+/**
+ * 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.cxf.dosgi.discovery.zookeeper;
+
+import java.util.Map;
+
+/**
+ * This interface allows transformation of service registration information before it is
pushed into the ZooKeeper
+ * discovery system.
+ * It can be useful for situations where a host name or port number needs to be changed in
cases where the host running
+ * the service is known differently from the outside to what the local Java process thinks
it is.
+ * Extra service properties can also be added to the registration which can be useful to
refine the remote service
+ * lookup process. <p/>
+ *
+ * DiscoveryPlugins use the OSGi WhiteBoard pattern. To add one to the system, register an
instance under this interface
+ * with the OSGi Service Registry. All registered DiscoveryPlugin instances are visited and
given a chance to process the
+ * information before it is pushed into ZooKeeper. <p/>
+ *
+ * Note that the changes made using this plugin do not modify the local service registration.
+ *
+ */
+public interface DiscoveryPlugin {
+    /**
+     * Process service registration information. Plugins can change this information before
it is published into the
+     * ZooKeeper discovery system.
+     *
+     * @param mutableProperties A map of service registration properties. The map is mutable
and any changes to the map
+     * will be reflected in the ZooKeeper registration.
+     * @param endpointKey The key under which the service is registered in ZooKeeper. This
key typically has the following
+     * format: hostname#port##context. While the actual value of this key is not actually
used by the system (people can use
+     * it as a hint to understand where the service is located), the value <i>must</i>
be unique for all services of a given type.
+     * @return The <tt>endpointKey</tt> value to be used. If there is no need
to change this simply return the value of the
+     * <tt>endpointKey</tt> parameter.
+     */
+    String process(Map<String, Object> mutableProperties, String endpointKey);
+}

Modified: cxf/dosgi/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/EndpointListenerImpl.java
URL: http://svn.apache.org/viewvc/cxf/dosgi/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/EndpointListenerImpl.java?rev=1353598&r1=1353597&r2=1353598&view=diff
==============================================================================
--- cxf/dosgi/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/EndpointListenerImpl.java
(original)
+++ cxf/dosgi/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/EndpointListenerImpl.java
Mon Jun 25 15:40:42 2012
@@ -1,20 +1,20 @@
-/** 
- * 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. 
+/**
+ * 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.cxf.dosgi.discovery.zookeeper;
 
@@ -24,33 +24,53 @@ import java.net.URISyntaxException;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import org.apache.cxf.dosgi.discovery.local.LocalDiscoveryUtils;
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
-import org.apache.zookeeper.ZooKeeper;
 import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooKeeper;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
 import org.osgi.service.remoteserviceadmin.EndpointDescription;
 import org.osgi.service.remoteserviceadmin.EndpointListener;
+import org.osgi.util.tracker.ServiceTracker;
 
 public class EndpointListenerImpl implements EndpointListener {
+    private final Logger LOG = Logger.getLogger(EndpointListenerImpl.class.getName());
 
-    private Logger LOG = Logger.getLogger(EndpointListenerImpl.class.getName());
-
-    private ZooKeeperDiscovery discovery;
-    private BundleContext bctx;
-
-    private List<EndpointDescription> endpoints = new ArrayList<EndpointDescription>();
-
+    private final ZooKeeperDiscovery discovery;
+    private final List<DiscoveryPlugin> discoveryPlugins = new CopyOnWriteArrayList<DiscoveryPlugin>();
+    private final ServiceTracker discoveryPluginTracker;
+    private final List<EndpointDescription> endpoints = new ArrayList<EndpointDescription>();
     private boolean closed = false;
 
     public EndpointListenerImpl(ZooKeeperDiscovery zooKeeperDiscovery, BundleContext bctx)
{
-        this.bctx = bctx;
         discovery = zooKeeperDiscovery;
+
+        discoveryPluginTracker = new ServiceTracker(bctx, DiscoveryPlugin.class.getName(),
null) {
+            @Override
+            public Object addingService(ServiceReference reference) {
+                Object svc = super.addingService(reference);
+                if (svc instanceof DiscoveryPlugin) {
+                    discoveryPlugins.add((DiscoveryPlugin) svc);
+                }
+                return svc;
+            }
+
+            @Override
+            public void removedService(ServiceReference reference, Object service) {
+                discoveryPlugins.remove(service);
+                super.removedService(reference, service);
+            }
+        };
+        discoveryPluginTracker.open();
     }
 
     private ZooKeeper getZooKeeper() {
@@ -77,12 +97,17 @@ public class EndpointListenerImpl implem
 
                 ZooKeeper zk = getZooKeeper();
                 for (String name : interfaces) {
+                    Map<String, Object> props = new HashMap<String, Object>(endpoint.getProperties());
+                    for (DiscoveryPlugin plugin : discoveryPlugins) {
+                        endpointKey = plugin.process(props, endpointKey);
+                    }
+
                     String path = Util.getZooKeeperPath(name);
+                    ensurePath(path, zk);
+
                     String fullPath = path + '/' + endpointKey;
                     LOG.fine("Creating ZooKeeper node: " + fullPath);
-
-                    ensurePath(path, zk);
-                    zk.create(fullPath, getData(endpoint), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
+                    zk.create(fullPath, getData(props), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
                 }
 
                 endpoints.add(endpoint);
@@ -146,9 +171,9 @@ public class EndpointListenerImpl implem
         }
     }
 
-    static byte[] getData(EndpointDescription sr) throws IOException {
-       String s = LocalDiscoveryUtils.getEndpointDescriptionXML(sr.getProperties());
-       return s.getBytes();
+    static byte[] getData(Map<String, Object> props) throws IOException {
+        String s = LocalDiscoveryUtils.getEndpointDescriptionXML(props);
+        return s.getBytes();
     }
 
     static String getKey(String endpoint) throws UnknownHostException, URISyntaxException
{
@@ -176,6 +201,6 @@ public class EndpointListenerImpl implem
             }
             endpoints.clear();
         }
+        discoveryPluginTracker.close();
     }
-
 }

Modified: cxf/dosgi/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/EndpointListenerImplTest.java
URL: http://svn.apache.org/viewvc/cxf/dosgi/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/EndpointListenerImplTest.java?rev=1353598&r1=1353597&r2=1353598&view=diff
==============================================================================
--- cxf/dosgi/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/EndpointListenerImplTest.java
(original)
+++ cxf/dosgi/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/EndpointListenerImplTest.java
Mon Jun 25 15:40:42 2012
@@ -1,36 +1,43 @@
-/** 
- * 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. 
+/**
+ * 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.cxf.dosgi.discovery.zookeeper;
 
+import java.lang.reflect.Field;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import junit.framework.TestCase;
 
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
-import org.apache.zookeeper.ZooKeeper;
 import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooKeeper;
+import org.easymock.IAnswer;
 import org.easymock.classextension.EasyMock;
 import org.easymock.classextension.IMocksControl;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
 import org.osgi.service.remoteserviceadmin.EndpointDescription;
 import org.osgi.service.remoteserviceadmin.RemoteConstants;
 
@@ -47,13 +54,13 @@ public class EndpointListenerImplTest ex
 
         EasyMock.expect(zkd.getZookeeper()).andReturn(zk).anyTimes();
 
-        
+
         String path = "/osgi/service_registry/myClass/google.de#80##test";
         EasyMock.expect(
                         zk.create(EasyMock.eq(path),
                                   (byte[])EasyMock.anyObject(), EasyMock.eq(Ids.OPEN_ACL_UNSAFE),
EasyMock
                                       .eq(CreateMode.EPHEMERAL))).andReturn("").once();
-        
+
         zk.delete(EasyMock.eq("/osgi/service_registry/myClass/google.de#80##test"), EasyMock.eq(-1));
         EasyMock.expectLastCall().once();
 
@@ -75,15 +82,89 @@ public class EndpointListenerImplTest ex
 
         eli.endpointRemoved(ed, null);
         eli.endpointRemoved(ed, null); // should do nothing
-        
+
         c.verify();
 
     }
-    
-    
+
+    public void testDiscoveryPlugin() throws Exception {
+        DiscoveryPlugin plugin1 = new DiscoveryPlugin() {
+            public String process(Map<String, Object> mutableProperties, String endpointKey)
{
+                String eid = (String) mutableProperties.get("endpoint.id");
+                mutableProperties.put("endpoint.id", eid + "/appended");
+                return endpointKey;
+            }
+        };
+        ServiceReference sr1 = EasyMock.createMock(ServiceReference.class);
+
+        DiscoveryPlugin plugin2 = new DiscoveryPlugin() {
+            public String process(Map<String, Object> mutableProperties, String endpointKey)
{
+                mutableProperties.put("foo", "bar");
+                return endpointKey.replaceAll("localhost", "some.machine");
+            }
+        };
+        ServiceReference sr2 = EasyMock.createMock(ServiceReference.class);
+
+        BundleContext ctx = EasyMock.createMock(BundleContext.class);
+        EasyMock.expect(ctx.createFilter(EasyMock.isA(String.class))).andAnswer(new IAnswer<Filter>()
{
+            public Filter answer() throws Throwable {
+                return FrameworkUtil.createFilter((String) EasyMock.getCurrentArguments()[0]);
+            }
+        }).anyTimes();
+        ctx.addServiceListener(EasyMock.isA(ServiceListener.class),
+                EasyMock.eq("(objectClass=org.apache.cxf.dosgi.discovery.zookeeper.DiscoveryPlugin)"));
+        EasyMock.expect(ctx.getService(sr1)).andReturn(plugin1).anyTimes();
+        EasyMock.expect(ctx.getService(sr2)).andReturn(plugin2).anyTimes();
+        EasyMock.expect(ctx.getServiceReferences(DiscoveryPlugin.class.getName(), null)).andReturn(
+                new ServiceReference[] {sr1, sr2}).anyTimes();
+        EasyMock.replay(ctx);
+
+        Map<String, Object> props = new HashMap<String, Object>();
+        props.put(Constants.OBJECTCLASS, new String[] { "org.foo.myClass" });
+        props.put(RemoteConstants.ENDPOINT_ID, "http://localhost:9876/test");
+        props.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, "myConfig");
+        EndpointDescription ep = new EndpointDescription(props);
+
+        Map<String, Object> expectedProps = new HashMap<String, Object>(props);
+        expectedProps.put("endpoint.id", "http://localhost:9876/test/appended");
+        expectedProps.put("foo", "bar");
+        expectedProps.put("service.imported", "true");
+
+        final ZooKeeper zk = EasyMock.createNiceMock(ZooKeeper.class);
+        String expectedFullPath = "/osgi/service_registry/org/foo/myClass/some.machine#9876##test";
+        EasyMock.expect(zk.create(
+                EasyMock.eq(expectedFullPath),
+                EasyMock.aryEq(EndpointListenerImpl.getData(expectedProps)),
+                EasyMock.eq(Ids.OPEN_ACL_UNSAFE),
+                EasyMock.eq(CreateMode.EPHEMERAL))).andReturn("");
+        EasyMock.replay(zk);
+        ZooKeeperDiscovery zkd = new ZooKeeperDiscovery(ctx, null) {
+            @Override
+            protected ZooKeeper getZookeeper() {
+                return zk;
+            }
+        };
+
+        EndpointListenerImpl eli = new EndpointListenerImpl(zkd, ctx);
+
+        List<EndpointDescription> endpointList = getEndpointsList(eli);
+        assertEquals("Precondition", 0, endpointList.size());
+        eli.endpointAdded(ep, null);
+        assertEquals(1, endpointList.size());
+
+        EasyMock.verify(zk);
+    }
+
+
+    @SuppressWarnings("unchecked")
+    private List<EndpointDescription> getEndpointsList(EndpointListenerImpl eli) throws
Exception {
+        Field field = eli.getClass().getDeclaredField("endpoints");
+        field.setAccessible(true);
+        return (List<EndpointDescription>) field.get(eli);
+    }
+
     public void testClose() throws KeeperException, InterruptedException{
-        
-        
+
         IMocksControl c = EasyMock.createNiceControl();
 
         BundleContext ctx = c.createMock(BundleContext.class);
@@ -93,13 +174,13 @@ public class EndpointListenerImplTest ex
 
         EasyMock.expect(zkd.getZookeeper()).andReturn(zk).anyTimes();
 
-        
+
         String path = "/osgi/service_registry/myClass/google.de#80##test";
         EasyMock.expect(
                         zk.create(EasyMock.eq(path),
                                   (byte[])EasyMock.anyObject(), EasyMock.eq(Ids.OPEN_ACL_UNSAFE),
EasyMock
                                       .eq(CreateMode.EPHEMERAL))).andReturn("").once();
-        
+
         zk.delete(EasyMock.eq("/osgi/service_registry/myClass/google.de#80##test"), EasyMock.eq(-1));
         EasyMock.expectLastCall().once();
 
@@ -119,15 +200,15 @@ public class EndpointListenerImplTest ex
         eli.endpointAdded(ed, null);
 
         eli.close(); // should result in zk.delete(...)
-        
+
         c.verify();
-        
+
     }
-    
-    
+
+
     public void testGetKey() throws Exception {
-      assertEquals("somehost#9090##org#example#TestEndpoint", 
-          EndpointListenerImpl.getKey("http://somehost:9090/org/example/TestEndpoint"));
-  }
+        assertEquals("somehost#9090##org#example#TestEndpoint",
+            EndpointListenerImpl.getKey("http://somehost:9090/org/example/TestEndpoint"));
+    }
 
 }

Modified: cxf/dosgi/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ClientServiceFactory.java
URL: http://svn.apache.org/viewvc/cxf/dosgi/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ClientServiceFactory.java?rev=1353598&r1=1353597&r2=1353598&view=diff
==============================================================================
--- cxf/dosgi/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ClientServiceFactory.java
(original)
+++ cxf/dosgi/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ClientServiceFactory.java
Mon Jun 25 15:40:42 2012
@@ -1,20 +1,20 @@
-/** 
- * 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. 
+/**
+ * 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.cxf.dosgi.dsw.handlers;
 
@@ -57,17 +57,17 @@ public class ClientServiceFactory implem
     public Object getService(final Bundle requestingBundle, final ServiceRegistration sreg)
{
         String interfaceName = sd.getInterfaces() != null && sd.getInterfaces().size()
> 0 ? (String)sd
             .getInterfaces().toArray()[0] : null;
-            
-        LOG.info("************ getService() from serviceFactory for " + interfaceName);
+
+        LOG.fine("getService() from serviceFactory for " + interfaceName);
 
         try {
             Object proxy = AccessController.doPrivileged(new PrivilegedAction<Object>()
{
-                public Object run() {                
+                public Object run() {
                      return handler.createProxy(sreg.getReference(), dswContext, requestingBundle
                                                        .getBundleContext(), iClass, sd);
                 }
             });
-            
+
             synchronized (this) {
                 ++serviceCounter;
             }



Mime
View raw message