| /******************************************************************************* |
| * Copyright (c) 2000, 2015 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.launching; |
| |
| |
| import java.io.BufferedInputStream; |
| import java.io.BufferedOutputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.Path; |
| |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| |
| import org.eclipse.debug.core.DebugEvent; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.IDebugEventSetListener; |
| import org.eclipse.debug.core.ILaunch; |
| import org.eclipse.debug.core.ILaunchConfiguration; |
| import org.eclipse.debug.core.model.IDebugTarget; |
| import org.eclipse.debug.core.model.IProcess; |
| |
| import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; |
| import org.eclipse.jdt.launching.JavaLaunchDelegate; |
| import org.eclipse.jdt.launching.JavaRuntime; |
| |
| public class JavaAppletLaunchConfigurationDelegate extends JavaLaunchDelegate implements IDebugEventSetListener { |
| |
| /** |
| * Mapping of ILaunch objects to File objects that represent the HTML file |
| * used to initiate the applet launch. This is used to delete the HTML |
| * file when the launch terminates. |
| */ |
| private static Map<ILaunch, File> fgLaunchToFileMap = new HashMap<>(); |
| |
| /** |
| * Used to map temporary file to launch object. |
| */ |
| private ILaunch fLaunch; |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.ILaunchConfigurationDelegate#launch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String, org.eclipse.debug.core.ILaunch, org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| @Override |
| public synchronized void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException { |
| try { |
| fLaunch = launch; |
| super.launch(configuration, mode, launch, monitor); |
| } catch (CoreException e) { |
| cleanup(launch); |
| throw e; |
| } |
| fLaunch = null; |
| } |
| |
| /** |
| * Returns the system property string for the policy file |
| * |
| * @param workingDir the working directory |
| * @return system property for the policy file |
| */ |
| public String getJavaPolicyFile(File workingDir) { |
| File file = new File(workingDir, "java.policy.applet");//$NON-NLS-1$ |
| if (!file.exists()) { |
| // copy it to the working directory |
| File test = LaunchingPlugin.getFileInPlugin(new Path("java.policy.applet")); //$NON-NLS-1$ |
| try (BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(file))) { |
| byte[] bytes = getFileByteContent(test); |
| outputStream.write(bytes); |
| } catch (IOException e) { |
| return "";//$NON-NLS-1$ |
| } |
| } |
| return "-Djava.security.policy=java.policy.applet";//$NON-NLS-1$ |
| } |
| |
| /** |
| * Using the specified launch configuration, build an HTML file that specifies the applet to launch. Return the name of the HTML file. |
| * |
| * @param configuration |
| * the launch config |
| * @param dir |
| * the directory in which to make the file |
| * @return the new HTML file |
| * @throws CoreException |
| * if the file cannot be built |
| */ |
| private File buildHTMLFile(ILaunchConfiguration configuration, File dir) throws CoreException { |
| String name = getAppletMainTypeName(configuration); |
| File tempFile = new File(dir, name + System.currentTimeMillis() + ".html"); //$NON-NLS-1$ |
| try (FileOutputStream stream = new FileOutputStream(tempFile)) { |
| String encoding = getLaunchManager().getEncoding(configuration); |
| StringBuffer buf = new StringBuffer(); |
| buf.append("<html>\n"); //$NON-NLS-1$ |
| buf.append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=" + encoding + "\"/>\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
| buf.append("<body>\n"); //$NON-NLS-1$ |
| buf.append("<applet code="); //$NON-NLS-1$ |
| buf.append(name); |
| buf.append(".class "); //$NON-NLS-1$ |
| String appletName = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_APPLET_NAME, ""); //$NON-NLS-1$ |
| if (appletName.length() != 0) { |
| buf.append("NAME =\"" + appletName + "\" "); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| buf.append("width=\""); //$NON-NLS-1$ |
| buf.append(Integer.toString(configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_APPLET_WIDTH, 200))); |
| buf.append("\" height=\""); //$NON-NLS-1$ |
| buf.append(Integer.toString(configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_APPLET_HEIGHT, 200))); |
| buf.append("\" >\n"); //$NON-NLS-1$ |
| Map<String, String> parameters = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_APPLET_PARAMETERS, new HashMap<String, String>()); |
| if (parameters.size() != 0) { |
| Iterator<Entry<String, String>> iterator = parameters.entrySet().iterator(); |
| while (iterator.hasNext()) { |
| Entry<String, String> next = iterator.next(); |
| buf.append("<param name="); //$NON-NLS-1$ |
| buf.append(getQuotedString(next.getKey())); |
| buf.append(" value="); //$NON-NLS-1$ |
| buf.append(getQuotedString(next.getValue())); |
| buf.append(">\n"); //$NON-NLS-1$ |
| } |
| } |
| buf.append("</applet>\n"); //$NON-NLS-1$ |
| buf.append("</body>\n"); //$NON-NLS-1$ |
| buf.append("</html>\n"); //$NON-NLS-1$ |
| |
| stream.write(buf.toString().getBytes(encoding)); |
| } catch(IOException e) { |
| LaunchingPlugin.log(e); |
| } |
| |
| return tempFile; |
| } |
| |
| private String getQuotedString(String string) { |
| int singleQuotes = count(string, '\''); |
| int doubleQuotes = count(string, '"'); |
| if (doubleQuotes == 0) { |
| return '"' + string + '"'; |
| } else if (singleQuotes == 0) { |
| return '\'' + string + '\''; |
| } else { |
| return '"' + convertToHTMLContent(string) + '"'; |
| } |
| } |
| |
| private static int count(String string, char character) { |
| int count = 0; |
| for (int i = 0; i < string.length(); i++) { |
| if (string.charAt(i) == character) { |
| count++; |
| } |
| } |
| return count; |
| } |
| |
| private static String convertToHTMLContent(String content) { |
| content = replace(content, '"', """); //$NON-NLS-1$ |
| content = replace(content, '\'', "'"); //$NON-NLS-1$ |
| return content; |
| } |
| |
| private static String replace(String text, char c, String s) { |
| |
| int previous = 0; |
| int current = text.indexOf(c, previous); |
| |
| if (current == -1) { |
| return text; |
| } |
| |
| StringBuffer buffer = new StringBuffer(); |
| while (current > -1) { |
| buffer.append(text.substring(previous, current)); |
| buffer.append(s); |
| previous = current + 1; |
| current = text.indexOf(c, previous); |
| } |
| buffer.append(text.substring(previous)); |
| |
| return buffer.toString(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.IDebugEventSetListener#handleDebugEvents(org.eclipse.debug.core.DebugEvent[]) |
| */ |
| @Override |
| public void handleDebugEvents(DebugEvent[] events) { |
| for (int i = 0; i < events.length; i++) { |
| DebugEvent event = events[i]; |
| Object eventSource = event.getSource(); |
| switch(event.getKind()) { |
| |
| // Delete the HTML file used for the launch |
| case DebugEvent.TERMINATE : |
| if (eventSource != null) { |
| ILaunch launch = null; |
| if (eventSource instanceof IProcess) { |
| IProcess process = (IProcess) eventSource; |
| launch = process.getLaunch(); |
| } else if (eventSource instanceof IDebugTarget) { |
| IDebugTarget debugTarget = (IDebugTarget) eventSource; |
| launch = debugTarget.getLaunch(); |
| } |
| if (launch != null) { |
| cleanup(launch); |
| } |
| } |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Cleans up event listener and temporary file for the launch. |
| * |
| * @param launch the launch |
| */ |
| private void cleanup(ILaunch launch) { |
| File temp = fgLaunchToFileMap.get(launch); |
| if (temp != null) { |
| try { |
| fgLaunchToFileMap.remove(launch); |
| temp.delete(); |
| } finally { |
| if (fgLaunchToFileMap.isEmpty()) { |
| DebugPlugin.getDefault().removeDebugEventListener(this); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns the contents of the given file as a byte array. |
| * @param file the file |
| * @return the byte array form the file |
| * @throws IOException if a problem occurred reading the file. |
| */ |
| protected static byte[] getFileByteContent(File file) throws IOException { |
| try (InputStream stream = new BufferedInputStream(new FileInputStream(file))) { |
| return getInputStreamAsByteArray(stream, (int) file.length()); |
| } |
| } |
| |
| /** |
| * Returns the given input stream's contents as a byte array. |
| * If a length is specified (ie. if length != -1), only length bytes |
| * are returned. Otherwise all bytes in the stream are returned. |
| * Note this doesn't close the stream. |
| * @param stream the stream |
| * @param length the length to read |
| * @return the byte array from the stream |
| * @throws IOException if a problem occurred reading the stream. |
| */ |
| protected static byte[] getInputStreamAsByteArray(InputStream stream, int length) |
| throws IOException { |
| byte[] contents; |
| if (length == -1) { |
| contents = new byte[0]; |
| int contentsLength = 0; |
| int bytesRead = -1; |
| do { |
| int available = stream.available(); |
| |
| // resize contents if needed |
| if (contentsLength + available > contents.length) { |
| System.arraycopy( |
| contents, |
| 0, |
| contents = new byte[contentsLength + available], |
| 0, |
| contentsLength); |
| } |
| |
| // read as many bytes as possible |
| bytesRead = stream.read(contents, contentsLength, available); |
| |
| if (bytesRead > 0) { |
| // remember length of contents |
| contentsLength += bytesRead; |
| } |
| } while (bytesRead > 0); |
| |
| // resize contents if necessary |
| if (contentsLength < contents.length) { |
| System.arraycopy( |
| contents, |
| 0, |
| contents = new byte[contentsLength], |
| 0, |
| contentsLength); |
| } |
| } else { |
| contents = new byte[length]; |
| int len = 0; |
| int readSize = 0; |
| while ((readSize != -1) && (len != length)) { |
| // See PR 1FMS89U |
| // We record first the read size. In this case len is the actual read size. |
| len += readSize; |
| readSize = stream.read(contents, len, length - len); |
| } |
| } |
| |
| return contents; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jdt.launching.AbstractJavaLaunchConfigurationDelegate#getProgramArguments(org.eclipse.debug.core.ILaunchConfiguration) |
| */ |
| @Override |
| public String getProgramArguments(ILaunchConfiguration configuration) throws CoreException { |
| File workingDir = verifyWorkingDirectory(configuration); |
| // Construct the HTML file and set its name as a program argument |
| File htmlFile = buildHTMLFile(configuration, workingDir); |
| if (htmlFile == null) { |
| abort(LaunchingMessages.JavaAppletLaunchConfigurationDelegate_Could_not_build_HTML_file_for_applet_launch_1, null, IJavaLaunchConfigurationConstants.ERR_COULD_NOT_BUILD_HTML); |
| } |
| // Add a debug listener if necessary |
| if (fgLaunchToFileMap.isEmpty()) { |
| DebugPlugin.getDefault().addDebugEventListener(this); |
| } |
| // Add a mapping of the launch to the html file |
| fgLaunchToFileMap.put(fLaunch, htmlFile); |
| return htmlFile.getName(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jdt.launching.AbstractJavaLaunchConfigurationDelegate#getVMArguments(org.eclipse.debug.core.ILaunchConfiguration) |
| */ |
| @Override |
| public String getVMArguments(ILaunchConfiguration configuration) throws CoreException { |
| StringBuffer arguments = new StringBuffer(super.getVMArguments(configuration)); |
| File workingDir = verifyWorkingDirectory(configuration); |
| String javaPolicyFile = getJavaPolicyFile(workingDir); |
| arguments.append(" "); //$NON-NLS-1$ |
| arguments.append(javaPolicyFile); |
| return arguments.toString(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jdt.launching.AbstractJavaLaunchConfigurationDelegate#getMainTypeName(org.eclipse.debug.core.ILaunchConfiguration) |
| */ |
| @Override |
| public String getMainTypeName(ILaunchConfiguration configuration) throws CoreException { |
| return configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_APPLET_APPLETVIEWER_CLASS, IJavaLaunchConfigurationConstants.DEFAULT_APPLETVIEWER_CLASS); |
| } |
| |
| /** |
| * Returns the applet's main type name. |
| * |
| * @param configuration the config |
| * @return the main type name |
| * @throws CoreException if a problem occurs |
| */ |
| protected String getAppletMainTypeName(ILaunchConfiguration configuration) throws CoreException { |
| return super.getMainTypeName(configuration); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jdt.launching.AbstractJavaLaunchConfigurationDelegate#getDefaultWorkingDirectory(org.eclipse.debug.core.ILaunchConfiguration) |
| */ |
| @Override |
| protected File getDefaultWorkingDirectory(ILaunchConfiguration configuration) throws CoreException { |
| // default working dir for applets is the project's output directory |
| String outputDir = JavaRuntime.getProjectOutputDirectory(configuration); |
| if (outputDir == null) { |
| // if no project attribute, default to eclipse directory |
| return new File(System.getProperty("user.dir")); //$NON-NLS-1$ |
| } |
| IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(outputDir); |
| if (resource == null || !resource.exists()) { |
| //default to eclipse directory |
| return new File(System.getProperty("user.dir")); //$NON-NLS-1$ |
| } |
| return resource.getLocation().toFile(); |
| } |
| |
| |
| } |