Commit ff609f6d authored by Derek DeMoro's avatar Derek DeMoro Committed by derek

1) "JM-432" Jive Messenger does not handle different web types (flash, applets, etc.)

2) Added Sound support in Live Assistant.

git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@2993 b35dd754-fafc-0310-a699-88a17e54d16e
parent c6a1fdca
......@@ -21,6 +21,7 @@ import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Collection;
/**
* ClassLoader for plugins. It searches the plugin directory for classes
......@@ -43,7 +44,7 @@ class PluginClassLoader {
* Constructs a plugin loader for the given plugin directory.
*
* @param pluginDir the plugin directory.
* @throws java.lang.SecurityException if the created class loader violates
* @throws SecurityException if the created class loader violates
* existing security constraints.
*/
public PluginClassLoader(File pluginDir) throws SecurityException {
......@@ -81,6 +82,10 @@ class PluginClassLoader {
}
}
public Collection<URL> getURLS(){
return list;
}
/**
* Adds a URL to the class loader. The {@link #initialize()} method should be called
* after adding the URL to make the change take effect.
......@@ -95,14 +100,22 @@ class PluginClassLoader {
* Initializes the class loader with all configured classpath URLs. This method
* can be called multiple times if the list of URLs changes.
*/
public void initialize(){
public void initialize() {
Iterator urls = list.iterator();
URL[] urlArray = new URL[list.size()];
for (int i = 0; urls.hasNext(); i++) {
urlArray[i] = (URL)urls.next();
}
// If the classloader is to be used by a child plugin, we should
// never use the ContextClassLoader, but only reuse the plugin classloader itself.
if (classLoader != null) {
classLoader = new URLClassLoader(urlArray, classLoader);
}
else {
classLoader = new URLClassLoader(urlArray, findParentClassLoader());
}
}
/**
* Load a class using this plugin class loader.
......
......@@ -187,6 +187,7 @@ public class PluginManager {
if (plugins.containsKey(parentPlugin)) {
pluginLoader = classloaders.get(getPlugin(parentPlugin));
pluginLoader.addDirectory(pluginDir);
}
else {
// See if the parent plugin exists but just hasn't been loaded yet.
......@@ -270,6 +271,15 @@ public class PluginManager {
String className = pluginXML.selectSingleNode("/plugin/class").getText();
plugin = (Plugin)pluginLoader.loadClass(className).newInstance();
if(parentPluginNode != null){
String parentPlugin = parentPluginNode.getTextTrim();
// See if the parent is already loaded.
if (plugins.containsKey(parentPlugin)) {
pluginLoader = classloaders.get(getPlugin(parentPlugin));
classloaders.put(plugin, pluginLoader);
}
}
plugin.initializePlugin(this, pluginDir);
plugins.put(pluginDir.getName(), plugin);
pluginDirs.put(plugin, pluginDir);
......@@ -312,25 +322,30 @@ public class PluginManager {
// If there a <adminconsole> section defined, register it.
Element adminElement = (Element)pluginXML.selectSingleNode("/plugin/adminconsole");
if (adminElement != null) {
String pluginName = pluginDir.getName();
if(parentPluginNode != null){
pluginName = parentPluginNode.getTextTrim();
}
// If global images are specified, override their URL.
Element imageEl = (Element)adminElement.selectSingleNode(
"/plugin/adminconsole/global/logo-image");
if (imageEl != null) {
imageEl.setText("plugins/" + pluginDir.getName() + "/" + imageEl.getText());
imageEl.setText("plugins/" + pluginName + "/" + imageEl.getText());
}
imageEl = (Element)adminElement.selectSingleNode(
"/plugin/adminconsole/global/login-image");
imageEl = (Element)adminElement.selectSingleNode("/plugin/adminconsole/global/login-image");
if (imageEl != null) {
imageEl.setText("plugins/" + pluginDir.getName() + "/" + imageEl.getText());
imageEl.setText("plugins/" + pluginName + "/" + imageEl.getText());
}
// Modify all the URL's in the XML so that they are passed through
// the plugin servlet correctly.
List urls = adminElement.selectNodes("//@url");
for (int i = 0; i < urls.size(); i++) {
Attribute attr = (Attribute)urls.get(i);
attr.setValue("plugins/" + pluginDir.getName() + "/" + attr.getValue());
attr.setValue("plugins/" + pluginName + "/" + attr.getValue());
}
AdminConsole.addModel(pluginDir.getName(), adminElement);
AdminConsole.addModel(pluginName, adminElement);
}
}
else {
......@@ -481,6 +496,15 @@ public class PluginManager {
return getElementValue(plugin, "/plugin/version");
}
/**
* Returns the classloader of a plugin.
* @param plugin the plugin.
* @return the classloader of the plugin.
*/
public PluginClassLoader getPluginClassloader(Plugin plugin){
return classloaders.get(plugin);
}
/**
* Returns the value of an element selected via an xpath expression from
* a Plugin's plugin.xml file.
......
......@@ -27,7 +27,14 @@ import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
......@@ -38,12 +45,12 @@ import java.util.concurrent.ConcurrentHashMap;
* to plugins. Since plugins can be dynamically loaded and live in a different place
* than normal Jive Messenger admin console files, it's not possible to have them
* added to the normal Jive Messenger admin console web app directory.<p>
*
* <p/>
* The servlet listens for requests in the form <tt>/plugins/[pluginName]/[JSP File]</tt>
* (e.g. <tt>/plugins/foo/example.jsp</tt>). It also listens for image requests in the
* the form <tt>/plugins/[pluginName]/images/*.png|gif</tt> (e.g.
* <tt>/plugins/foo/images/example.gif</tt>).<p>
*
* <p/>
* JSP files must be compiled and available via the plugin's class loader. The mapping
* between JSP name and servlet class files is defined in [pluginName]/web/web.xml.
* Typically, this file is auto-generated by the JSP compiler when packaging the plugin.
......@@ -68,8 +75,7 @@ public class PluginServlet extends HttpServlet {
}
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
throws ServletException, IOException {
String pathInfo = request.getPathInfo();
if (pathInfo == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
......@@ -85,20 +91,18 @@ public class PluginServlet extends HttpServlet {
handleJSP(pathInfo, request, response);
return;
}
// Handle image requests.
else if (pathInfo.endsWith(".gif") || pathInfo.endsWith(".png")) {
handleImage(pathInfo, response);
return;
}
// Handle servlet requests.
else if (getServlet(pathInfo) != null) {
handleServlet(pathInfo, request, response);
}
// Anything else results in a 404.
// Handle image requests.
else {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
handleOtherRequest(pathInfo, response);
return;
}
// Anything else results in a 404.
}
catch (Exception e) {
Log.error(e);
......@@ -220,11 +224,11 @@ public class PluginServlet extends HttpServlet {
* @throws IOException if an IOException occurs while handling the request.
*/
private void handleJSP(String pathInfo, HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException
{
HttpServletResponse response) throws ServletException, IOException {
// Strip the starting "/" from the path to find the JSP URL.
String jspURL = pathInfo.substring(1);
HttpServlet servlet = servlets.get(jspURL);
if (servlet != null) {
servlet.service(request, response);
......@@ -286,24 +290,39 @@ public class PluginServlet extends HttpServlet {
return servlet;
}
/**
* Handles a request for an image.
* Handles a request for other web items (images, flash, applets, etc.)
*
* @param pathInfo the extra path info.
* @param response the response object.
* @throws IOException if an IOException occurs while handling the request.
*/
private void handleImage(String pathInfo, HttpServletResponse response) throws IOException {
private void handleOtherRequest(String pathInfo, HttpServletResponse response) throws IOException {
String[] parts = pathInfo.split("/");
// Image request must be in correct format.
if (parts.length != 4) {
if (parts.length < 3) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
String contextPath = "";
int index = pathInfo.indexOf(parts[1]);
if (index != -1) {
contextPath = pathInfo.substring(index + parts[1].length());
}
File pluginDirectory = new File(JiveGlobals.getHomeDirectory(), "plugins");
File image = new File(pluginDirectory, parts[1] + File.separator + "web" +
File.separator + "images" + File.separator + parts[3]);
if (!image.exists()) {
File file = new File(pluginDirectory, parts[1] + File.separator + "web" + contextPath);
// When using dev environment, the images dir may be under something other that web.
Plugin plugin = pluginManager.getPlugin(parts[1]);
PluginDevEnvironment environment = pluginManager.getDevEnvironment(plugin);
if (environment != null) {
file = new File(environment.getWebRoot(), contextPath);
}
if (!file.exists()) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
......@@ -313,17 +332,20 @@ public class PluginServlet extends HttpServlet {
if (pathInfo.endsWith(".png")) {
contentType = "image/png";
}
response.setHeader("Content-disposition", "filename=\"" + image + "\";");
else if (pathInfo.endsWith(".swf")) {
contentType = "application/x-shockwave-flash";
}
response.setHeader("Content-disposition", "filename=\"" + file + "\";");
response.setContentType(contentType);
// Write out the image to the user.
InputStream in = null;
ServletOutputStream out = null;
try {
in = new BufferedInputStream(new FileInputStream(image));
in = new BufferedInputStream(new FileInputStream(file));
out = response.getOutputStream();
// Set the size of the file.
response.setContentLength((int)image.length());
response.setContentLength((int)file.length());
// Use a 1K buffer.
byte[] buf = new byte[1024];
......@@ -337,18 +359,17 @@ public class PluginServlet extends HttpServlet {
in.close();
}
catch (Exception ignored) {
// Ignore.
}
try {
out.close();
}
catch (Exception ignored) {
// Ignore.
}
}
}
}
/**
* Handles a request for a JSP page in development mode. If development mode is
* not enabled, this method returns false so that normal JSP handling can be performed.
......@@ -361,8 +382,7 @@ public class PluginServlet extends HttpServlet {
* @return true if this page request was handled; false if the request was not handled.
*/
private boolean handleDevJSP(String pathInfo, HttpServletRequest request,
HttpServletResponse response)
{
HttpServletResponse response) {
String jspURL = pathInfo.substring(1);
// Handle pre-existing pages and fail over to pre-compiled pages.
......@@ -447,28 +467,40 @@ public class PluginServlet extends HttpServlet {
* @return the classpath needed to compile a single jsp in a plugin.
*/
private static String getClasspathForPlugin(Plugin plugin) {
StringBuilder builder = new StringBuilder();
final StringBuilder classpath = new StringBuilder();
File pluginDirectory = pluginManager.getPluginDirectory(plugin);
PluginDevEnvironment env = pluginManager.getDevEnvironment(plugin);
PluginDevEnvironment pluginEnv = pluginManager.getDevEnvironment(plugin);
PluginClassLoader pluginClassloader = pluginManager.getPluginClassloader(plugin);
Collection col = pluginClassloader.getURLS();
for (Object aCol : col) {
URL url = (URL)aCol;
File file = new File(url.getFile());
classpath.append(file.getAbsolutePath().toString()).append(";");
}
// Load all jars from lib
File libDirectory = new File(pluginDirectory, "lib");
File[] libs = libDirectory.listFiles();
for (File libFile : libs) {
builder.append(libFile.getAbsolutePath()).append(';');
final int no = libs != null ? libs.length : 0;
for (int i = 0; i < no; i++) {
File libFile = libs[i];
classpath.append(libFile.getAbsolutePath()).append(';');
}
File messengerRoot = pluginDirectory.getParentFile().getParentFile().getParentFile();
File messengerLib = new File(messengerRoot, "target//lib");
builder.append(messengerLib.getAbsolutePath()).append("//servlet.jar;");
builder.append(messengerLib.getAbsolutePath()).append("//messenger.jar;");
builder.append(messengerLib.getAbsolutePath()).append("//jasper-compiler.jar;");
builder.append(messengerLib.getAbsolutePath()).append("//jasper-runtime.jar;");
builder.append(env.getClassesDir().getAbsolutePath()).append(";");
classpath.append(messengerLib.getAbsolutePath()).append("//servlet.jar;");
classpath.append(messengerLib.getAbsolutePath()).append("//messenger.jar;");
classpath.append(messengerLib.getAbsolutePath()).append("//jasper-compiler.jar;");
classpath.append(messengerLib.getAbsolutePath()).append("//jasper-runtime.jar;");
classpath.append(pluginEnv.getClassesDir().getAbsolutePath()).append(";");
return builder.toString();
return classpath.toString();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment