Added proxying to the application to enabled deferred service retrieval.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@559558 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/examples/servicebased.host/src/main/java/org/apache/felix/example/servicebased/host/DefaultShape.java b/examples/servicebased.host/src/main/java/org/apache/felix/example/servicebased/host/DefaultShape.java
new file mode 100644
index 0000000..61c0118
--- /dev/null
+++ b/examples/servicebased.host/src/main/java/org/apache/felix/example/servicebased/host/DefaultShape.java
@@ -0,0 +1,142 @@
+/*
+ * 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.example.servicebased.host;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import javax.swing.ImageIcon;
+import org.apache.felix.example.servicebased.host.service.SimpleShape;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * This class is used as a proxy to defer object creation from shape
+ * provider bundles and also as a placeholder shape when previously
+ * used shapes are no longer available. These two purposes are
+ * actually orthogonal, but were combined into a single class to
+ * reduce the number of classes in the application. The proxy-related
+ * functionality is introduced as a way to lazily create shape
+ * objects in an effort to improve performance; this level of
+ * indirection could be removed if eager creation of objects is not
+ * a concern. Since this application uses the service-based extension
+ * appraoch, lazy shape creation will only come into effect if
+ * service providers register service factories instead of directly
+ * registering <tt>SimpleShape</tt> or if they use a technology like
+ * Declarative Services or iPOJO to register services. Since the
+ * example providers register services instances directly there is
+ * no laziness in the example, but the proxy approach is still used
+ * to demonstrate how to make laziness possible and to keep it
+ * similar to the extender-based approach.
+**/
+class DefaultShape implements SimpleShape
+{
+    private SimpleShape m_shape;
+    private ImageIcon m_icon;
+    private BundleContext m_context;
+    private ServiceReference m_ref;
+
+    /**
+     * This constructs a placeholder shape that draws a default
+     * icon. It is used when a previously drawn shape is no longer
+     * available.
+    **/
+    public DefaultShape()
+    {
+        // Do nothing.
+    }
+
+    /**
+     * This constructs a proxy shape that lazily gets the shape service.
+     * @param context The bundle context to use for retrieving the shape service.
+     * @param ref The service reference of the service.
+    **/
+    public DefaultShape(BundleContext context, ServiceReference ref)
+    {
+        m_context = context;
+        m_ref = ref;
+    }
+
+    /**
+     * This method tells the proxy to dispose of its service object; this
+     * is called when the underlying service goes away.
+    **/
+    public void dispose()
+    {
+        if (m_shape != null)
+        {
+            m_context.ungetService(m_ref);
+            m_context = null;
+            m_ref = null;
+            m_shape = null;
+        }
+    }
+
+    /**
+     * Implements the <tt>SimpleShape</tt> interface method. When acting as
+     * a proxy, this method gets the shape service and then uses it to draw
+     * the shape. When acting as a placeholder shape, this method draws the
+     * default icon.
+     * @param g2 The graphics object used for painting.
+     * @param p The position to paint the triangle.
+    **/
+    public void draw(Graphics2D g2, Point p)
+    {
+        // If this is a proxy shape, instantiate the shape class
+        // and use it to draw the shape.
+        if (m_context != null)
+        {
+            try
+            {
+                if (m_shape == null)
+                {
+                    // Get the shape service.
+                    m_shape = (SimpleShape) m_context.getService(m_ref);
+                }
+                // Draw the shape.
+                m_shape.draw(g2, p);
+                // If everything was successful, then simply return.
+                return;
+            }
+            catch (Exception ex)
+            {
+                // This generally should not happen, but if it does then
+                // we can just fall through and paint the default icon.
+            }
+        }
+
+        // If the proxied shape could not be drawn for any reason or if
+        // this shape is simply a placeholder, then draw the default icon.
+        if (m_icon == null)
+        {
+            try
+            {
+                m_icon = new ImageIcon(this.getClass().getResource("underc.png"));
+            }
+            catch (Exception ex)
+            {
+                ex.printStackTrace();
+                g2.setColor(Color.red);
+                g2.fillRect(0, 0, 60, 60);
+                return;
+            }
+        }
+        g2.drawImage(m_icon.getImage(), 0, 0, null);
+    }
+}
\ No newline at end of file
diff --git a/examples/servicebased.host/src/main/java/org/apache/felix/example/servicebased/host/DrawingFrame.java b/examples/servicebased.host/src/main/java/org/apache/felix/example/servicebased/host/DrawingFrame.java
index 29d1d59..82c079b 100644
--- a/examples/servicebased.host/src/main/java/org/apache/felix/example/servicebased/host/DrawingFrame.java
+++ b/examples/servicebased.host/src/main/java/org/apache/felix/example/servicebased/host/DrawingFrame.java
@@ -26,6 +26,13 @@
 
 import org.apache.felix.example.servicebased.host.service.SimpleShape;
 
+/**
+ * This class represents the main application class, which is a JFrame subclass
+ * that manages a toolbar of shapes and a drawing canvas. This class does not
+ * directly interact with the underlying OSGi framework; instead, it is injected
+ * with the available <tt>SimpleShape</tt> instances to eliminate any
+ * dependencies on the OSGi application programming interfaces.
+**/
 public class DrawingFrame extends JFrame
     implements MouseListener, MouseMotionListener
 {
@@ -34,11 +41,14 @@
     private JToolBar m_toolbar;
     private String m_selected;
     private JPanel m_panel;
-    private Map m_shapes = new HashMap();
     private ShapeComponent m_selectedComponent;
+    private Map m_shapes = new HashMap();
     private SimpleShape m_defaultShape = new DefaultShape();
     private ActionListener m_reusableActionListener = new ShapeActionListener();
 
+    /**
+     * Default constructor that populates the main window.
+    **/
     public DrawingFrame()
     {
         super("Service-Based Host");
@@ -55,11 +65,22 @@
         setSize(400, 400);
     }
 
+    /**
+     * This method sets the currently selected shape to be used for drawing
+     * on the canvas.
+     * @param name The name of the shape to use for drawing on the canvas.
+    **/
     public void selectShape(String name)
     {
         m_selected = name;
     }
 
+    /**
+     * Retrieves the available <tt>SimpleShape</tt> associated with the given name.
+     * @param name The name of the <tt>SimpleShape</tt> to retrieve.
+     * @return The corresponding <tt>SimpleShape</tt> instance if available or
+     *         <tt>null</tt>.
+    **/
     public SimpleShape getShape(String name)
     {
         ShapeInfo info = (ShapeInfo) m_shapes.get(name);
@@ -73,6 +94,12 @@
         }
     }
 
+    /**
+     * Injects an available <tt>SimpleShape</tt> into the drawing frame.
+     * @param name The name of the injected <tt>SimpleShape</tt>.
+     * @param icon The icon associated with the injected <tt>SimpleShape</tt>.
+     * @param shape The injected <tt>SimpleShape</tt> instance.
+    **/
     public void addShape(String name, Icon icon, SimpleShape shape)
     {
         m_shapes.put(name, new ShapeInfo(name, icon, shape));
@@ -90,6 +117,10 @@
         repaint();
     }
 
+    /**
+     * Removes a no longer available <tt>SimpleShape</tt> from the drawing frame.
+     * @param name The name of the <tt>SimpleShape</tt> to remove.
+    **/
     public void removeShape(String name)
     {
         m_shapes.remove(name);
@@ -118,6 +149,11 @@
         }
     }
 
+    /**
+     * Implements method for the <tt>MouseListener</tt> interface to
+     * draw the selected shape into the drawing canvas.
+     * @param evt The associated mouse event.
+    **/
     public void mouseClicked(MouseEvent evt)
     {
         if (m_selected == null)
@@ -135,14 +171,27 @@
         }
     }
 
+    /**
+     * Implements an empty method for the <tt>MouseListener</tt> interface.
+     * @param evt The associated mouse event.
+    **/
     public void mouseEntered(MouseEvent evt)
     {
     }
 
+    /**
+     * Implements an empty method for the <tt>MouseListener</tt> interface.
+     * @param evt The associated mouse event.
+    **/
     public void mouseExited(MouseEvent evt)
     {
     }
 
+    /**
+     * Implements method for the <tt>MouseListener</tt> interface to initiate
+     * shape dragging.
+     * @param evt The associated mouse event.
+    **/
     public void mousePressed(MouseEvent evt)
     {
         Component c = m_panel.getComponentAt(evt.getPoint());
@@ -155,6 +204,11 @@
         }
     }
 
+    /**
+     * Implements method for the <tt>MouseListener</tt> interface to complete
+     * shape dragging.
+     * @param evt The associated mouse event.
+    **/
     public void mouseReleased(MouseEvent evt)
     {
         if (m_selectedComponent != null)
@@ -168,45 +222,31 @@
         }
     }
 
+    /**
+     * Implements method for the <tt>MouseMotionListener</tt> interface to
+     * move a dragged shape.
+     * @param evt The associated mouse event.
+    **/
     public void mouseDragged(MouseEvent evt)
     {
         m_selectedComponent.setBounds(
             evt.getX() - BOX / 2, evt.getY() - BOX / 2, BOX, BOX);
     }
 
+    /**
+     * Implements an empty method for the <tt>MouseMotionListener</tt>
+     * interface.
+     * @param evt The associated mouse event.
+    **/
     public void mouseMoved(MouseEvent evt)
     {
     }
 
-    private class DefaultShape implements SimpleShape
-    {
-        private ImageIcon m_icon = null;
-
-        public void draw(Graphics2D g2, Point p)
-        {
-            if (m_icon == null)
-            {
-                try
-                {
-                    m_icon = new ImageIcon(this.getClass().getResource("underc.png"));
-                }
-                catch (Exception ex)
-                {
-                    ex.printStackTrace();
-                    g2.setColor(Color.red);
-                    g2.fillRect(0, 0, getWidth() - 1, getHeight() - 1);
-                    return;
-                }
-            }
-            g2.drawImage(m_icon.getImage(), 0, 0, null);
-        }
-
-        public String getName()
-        {
-            return "Default";
-        }
-    }
-
+    /**
+     * Simple action listener for shape tool bar buttons that sets
+     * the drawing frame's currently selected shape when receiving
+     * an action event.
+    **/
     private class ShapeActionListener implements ActionListener
     {
         public void actionPerformed(ActionEvent evt)
@@ -215,6 +255,10 @@
         }
     }
 
+    /**
+     * This class is used to record the various information pertaining to
+     * an available shape.
+    **/
     private static class ShapeInfo
     {
         public String m_name;
diff --git a/examples/servicebased.host/src/main/java/org/apache/felix/example/servicebased/host/ShapeTracker.java b/examples/servicebased.host/src/main/java/org/apache/felix/example/servicebased/host/ShapeTracker.java
index ff97a85..0a76971 100644
--- a/examples/servicebased.host/src/main/java/org/apache/felix/example/servicebased/host/ShapeTracker.java
+++ b/examples/servicebased.host/src/main/java/org/apache/felix/example/servicebased/host/ShapeTracker.java
@@ -41,6 +41,8 @@
     private static final int MODIFIED = 2;
     // Flag indicating a removed shape.
     private static final int REMOVED = 3;
+    // The bundle context used for tracking.
+    private BundleContext m_context;
     // The application object to notify.
     private DrawingFrame m_frame;
 
@@ -54,6 +56,7 @@
     public ShapeTracker(BundleContext context, DrawingFrame frame)
     {
         super(context, SimpleShape.class.getName(), null);
+        m_context = context;
         m_frame = frame;
     }
 
@@ -65,7 +68,7 @@
     **/
     public Object addingService(ServiceReference ref)
     {
-        SimpleShape shape = (SimpleShape) super.addingService(ref);
+        SimpleShape shape = new DefaultShape(m_context, ref);
         processShapeOnEventThread(ADDED, ref, shape);
         return shape;
     }
@@ -90,7 +93,7 @@
     public void removedService(ServiceReference ref, Object svc)
     {
         processShapeOnEventThread(REMOVED, ref, (SimpleShape) svc);
-        super.removedService(ref, svc);
+        ((DefaultShape) svc).dispose();
     }
 
     /**