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