Modified extender application to use a proxy shape to defer object
creation.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@559539 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/DefaultShape.java b/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/DefaultShape.java
new file mode 100644
index 0000000..e889fbb
--- /dev/null
+++ b/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/DefaultShape.java
@@ -0,0 +1,126 @@
+/*
+ * 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.extenderbased.host;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import javax.swing.ImageIcon;
+import org.apache.felix.example.extenderbased.host.extension.SimpleShape;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * 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.
+**/
+class DefaultShape implements SimpleShape
+{
+ private SimpleShape m_shape;
+ private ImageIcon m_icon;
+ private BundleContext m_context;
+ private long m_bundleId;
+ private String m_className;
+
+ /**
+ * 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 instantiates the
+ * shape class from the associated extension bundle.
+ * @param context The bundle context to use for retrieving the extension bundle.
+ * @param bundleId The bundle ID of the extension bundle.
+ * @param className The class name of the shape.
+ **/
+ public DefaultShape(BundleContext context, long bundleId, String className)
+ {
+ m_context = context;
+ m_bundleId = bundleId;
+ m_className = className;
+ }
+
+ /**
+ * Implements the <tt>SimpleShape</tt> interface method. When acting as
+ * a proxy, this method lazily loads and instantiates the shape class 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 bundle.
+ Bundle bundle = m_context.getBundle(m_bundleId);
+ // Load the class and instantiate it.
+ Class clazz = bundle.loadClass(m_className);
+ m_shape = (SimpleShape) clazz.newInstance();
+ }
+ // 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/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/DrawingFrame.java b/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/DrawingFrame.java
index c17fcef..1b4ce60 100644
--- a/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/DrawingFrame.java
+++ b/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/DrawingFrame.java
@@ -26,6 +26,13 @@
import org.apache.felix.example.extenderbased.host.extension.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("Extender-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/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/ShapeTracker.java b/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/ShapeTracker.java
index 09d8f44..5f8390a 100644
--- a/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/ShapeTracker.java
+++ b/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/ShapeTracker.java
@@ -38,6 +38,8 @@
private static final int ADDED = 1;
// Flag indicating a removed shape.
private static final int REMOVED = 2;
+ // The bundle context used for tracking.
+ private BundleContext m_context;
// The application object to notify.
private DrawingFrame m_frame;
@@ -51,6 +53,7 @@
public ShapeTracker(BundleContext context, DrawingFrame frame)
{
super(context);
+ m_context = context;
m_frame = frame;
}
@@ -126,16 +129,11 @@
String iconPath = (String) dict.get(SimpleShape.ICON_PROPERTY);
Icon icon = new ImageIcon(bundle.getResource(iconPath));
// Get the class of the extension.
- String classPath = (String) dict.get(SimpleShape.CLASS_PROPERTY);
- try
- {
- Class clazz = bundle.loadClass(classPath);
- m_frame.addShape(name, icon, (SimpleShape) clazz.newInstance());
- }
- catch (Exception ex)
- {
- // This should never happen in this example.
- }
+ String className = (String) dict.get(SimpleShape.CLASS_PROPERTY);
+ m_frame.addShape(
+ name,
+ icon,
+ new DefaultShape(m_context, bundle.getBundleId(), className));
break;
case REMOVED: