blob: a4c1880e5e3f3bd34e80923a0b087e259b40c70f [file] [log] [blame]
/*
<<<<<<< Applet.java
* $Id$ $Revision$
* $Date$
=======
* $Id$ $Revision:
* 1.71 $ $Date$
>>>>>>> 1.5
*
* ==============================================================================
* Licensed 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 wicket.markup.html.applet;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.List;
import wicket.IResourceListener;
import wicket.Resource;
import wicket.ResourceReference;
import wicket.markup.ComponentTag;
import wicket.markup.MarkupStream;
import wicket.markup.html.WebComponent;
import wicket.markup.html.form.IFormSubmitListener;
import wicket.markup.parser.XmlTag;
import wicket.model.IModel;
import wicket.protocol.http.IMultipartWebRequest;
import wicket.protocol.http.WebRequest;
import wicket.protocol.http.WebResponse;
import wicket.resource.ByteArrayResource;
import wicket.resource.JarResourceFactory;
import wicket.util.lang.Bytes;
import wicket.util.lang.Objects;
import wicket.util.string.Strings;
import wicket.util.upload.FileItem;
/**
* This component integrates Swing tightly with Wicket by automatically
* generating Swing applets on demand. An Applet is initialized on the client
* side by the class passed to the Applet constructor. This class must implement
* the IApplet interface.
* <p>
* The JAR file for a given Applet is automatically created by finding the
* closure of class files statically referenced by the IApplet class. If any
* classes are dynamically loaded by an Applet, they can be added to the JAR by
* calling Applet.addClass(Class). The IApplet class will only be
* (automatically) instantiated in the client browser's VM by the host applet
* container implementation HostApplet. The structure and implementation of
* HostApplet, however, is an internal implementation detail to Wicket and might
* be changed in the future.
* <p>
* Once the JAR file for a given Applet subclass has been created, it is reused
* for all future instances of that Applet class. The auto-created JAR is
* referenced by each instance of the Applet by automatically modifying the
* APPLET tag that the Applet is attached to. The result is a fully automatic
* applet. Any additional attributes you set on the APPLET tag in your HTML such
* as width and height will be left unchanged.
* <p>
* To add Swing behavior to an Applet, the user's implementation of IApplet
* should populate the Container passed to the IApplet.init() method with Swing
* components. Those components should edit the model passed into the same
* init() method. The model can be pushed back to the server at any time (via an
* internally executed form POST over HTTP) by manually calling the
* setModel(Object) method on the IAppletServer interface passed to the
* IApplet.init() method. Such a manual update is not necessary if the Applet
* component is contained in a Form. For Applets nested within Forms, the form
* submit will result in an automatic call via JavaScript to the
* IAppletServer.setModel() method before the Form itself POSTs. Therefore, the
* Applet's model will be updated by the time Form.onSubmit() is called. This
* allows users to augment HTML forms with Applet based Swing components in a
* modular and reusable fashion.
*
* @author Jonathan Locke
*/
public class Applet extends WebComponent implements IResourceListener, IFormSubmitListener
{
private static final long serialVersionUID = 1L;
/** Root class for applet JAR */
private Class appletClass;
/** Extra root classes for applet JAR to handle dynamic loading */
private List/* <Class> */classes = new ArrayList(2);
/**
* Construct.
*
* @param id
* The component id
* @param appletClass
* The class that implement's this applet
*/
public Applet(final String id, final Class appletClass)
{
super(id);
addAppletClass(appletClass);
}
/**
* Construct.
*
* @param id
* The component id
* @param model
* The wicket model
* @param appletClass
* The class that implements this applet's initialization
*/
public Applet(final String id, final IModel model, final Class appletClass)
{
super(id, model);
addAppletClass(appletClass);
}
/**
* Add class root to applet. It should only be necessary to call this method
* in the rare case that your applet does dynamic class loading.
*
* @param c
* The class to add
*/
public void addClass(final Class c)
{
classes.add(c);
}
/**
* By default, an Applet's model is the Wicket model. In the case of some
* Sprockets, the model may be something entirely different that depends on
* the communication needs of the individual component.
*
* @return The model for this applet
*/
public Object getAppletModel()
{
return getModelObject();
}
/**
* THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL IT.
*
* Called when model data is posted back from the client applet. This method
* reads the posted object from the form data and calls setAppletModel()
* with that object.
*/
public void onFormSubmitted()
{
// Get request
final WebRequest webRequest = ((WebRequest)getRequest()).newMultipartWebRequest(Bytes.MAX);
getRequestCycle().setRequest(webRequest);
// Get the item for the path
final FileItem item = ((IMultipartWebRequest)webRequest).getFile("model");
try
{
final InputStream in = item.getInputStream();
try
{
final Object model = new ObjectInputStream(in).readObject();
System.out.println("Setting model to " + model);
setAppletModel(model);
}
finally
{
in.close();
}
}
catch (ClassNotFoundException e)
{
((WebResponse)getResponse()).setHeader("STATUS", "417");
}
catch (IOException e)
{
((WebResponse)getResponse()).setHeader("STATUS", "417");
}
}
/**
* THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL IT.
*
* Called when model data is retrieved by the client applet. This method
* writes the model retrieved by getAppletModel() to the client as an
* application/x-wicket-model binary resource. The HostApplet implementation
* on the client then reads the object and updates the sprocket's model
* appropriately.
*
* @see wicket.IResourceListener#onResourceRequested()
*/
public void onResourceRequested()
{
new ByteArrayResource("application/x-wicket-model", Objects
.objectToByteArray(getAppletModel())).onResourceRequested();
}
/**
* By default, an Applet's model is the Wicket model. In the case of some
* Sprockets, the model may be something entirely different that depends on
* the communication needs of the individual component.
*
* @param model
* The model to set
*/
public void setAppletModel(final Object model)
{
setModelObject(model);
}
/**
* @return Height of applet, or -1 to use default provided in html tag
*/
protected int getHeight()
{
return -1;
}
/**
* @return Width of applet, or -1 to use default provided in html tag
*/
protected int getWidth()
{
return -1;
}
/**
* @see wicket.Component#onComponentTag(wicket.markup.ComponentTag)
*/
protected void onComponentTag(final ComponentTag tag)
{
checkComponentTag(tag, "applet");
tag.put("code", HostApplet.class.getName());
final String jarName = appletClass.getName() + ".jar";
final ResourceReference jarResourceReference = new ResourceReference(jarName)
{
protected Resource newResource()
{
// Create JAR resource
final JarResourceFactory factory = new JarResourceFactory();
factory.addClassClosures(classes);
return factory.getResource();
}
};
final String jarResourceUrl = getRequestCycle().urlFor(jarResourceReference);
final String codebase = Strings.beforeLastPathComponent(jarResourceUrl, '/') + '/';
tag.put("codebase", codebase);
tag.put("archive", jarName);
tag.put("name", getPageRelativePath().replace(':', '_'));
final int width = getWidth();
if (width != -1)
{
tag.put("width", width);
}
final int height = getHeight();
if (height != -1)
{
tag.put("height", height);
}
tag.setType(XmlTag.OPEN);
super.onComponentTag(tag);
}
/**
* @see wicket.Component#onComponentTagBody(wicket.markup.MarkupStream,
* wicket.markup.ComponentTag)
*/
protected void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag)
{
replaceComponentTagBody(markupStream, openTag, "\n<param name=\"getModelUrl\" value=\""
+ urlFor(IResourceListener.INTERFACE) + "\"/>"
+ "\n<param name=\"setModelUrl\" value=\"" + urlFor(IFormSubmitListener.INTERFACE)
+ "\"/>" + "\n<param name=\"appletClassName\" value=\"" + appletClass.getName()
+ "\"/>\n");
}
/**
* @param appletClass
* The class to add
*/
private void addAppletClass(final Class appletClass)
{
// Applet code must implement IAppletCode interface
if (!IApplet.class.isAssignableFrom(appletClass))
{
throw new IllegalArgumentException("Applet class " + appletClass.getName()
+ " must implement " + IApplet.class.getName());
}
this.appletClass = appletClass;
addClass(appletClass);
addClass(HostApplet.class);
}
}