diff --git a/tools/demobench/build.gradle b/tools/demobench/build.gradle index 8d71db2137..1eb6b3a8d8 100644 --- a/tools/demobench/build.gradle +++ b/tools/demobench/build.gradle @@ -189,6 +189,10 @@ task javapackage(dependsOn: distZip) { include(name: 'plugins/*.jar') include(name: 'explorer/*.jar') } + + fileset(dir: "$pkg_source/package", type: 'data') { + include(name: "bugfixes/**") + } } // This is specific to MacOSX packager. diff --git a/tools/demobench/package-demobench-exe.bat b/tools/demobench/package-demobench-exe.bat index e33d048e5d..c2f0b41e80 100644 --- a/tools/demobench/package-demobench-exe.bat +++ b/tools/demobench/package-demobench-exe.bat @@ -8,8 +8,8 @@ if not defined JAVA_HOME goto NoJavaHome set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. -call %DIRNAME%\..\..\gradlew -PpackageType=exe javapackage -if "%ERRORLEVEL%" neq "0" goto Fail +call %DIRNAME%\..\..\gradlew -PpackageType=exe javapackage %* +if ERRORLEVEL 1 goto Fail @echo @echo Wrote installer to %DIRNAME%\build\javapackage\bundles\ @echo diff --git a/tools/demobench/package/bugfixes/apply.bat b/tools/demobench/package/bugfixes/apply.bat new file mode 100644 index 0000000000..f770222122 --- /dev/null +++ b/tools/demobench/package/bugfixes/apply.bat @@ -0,0 +1,39 @@ +@echo off + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. + +set SOURCEDIR=%DIRNAME%java +set BUILDDIR=%DIRNAME%build + +if '%1' == '' ( + @echo Need location of rt.jar + exit /b 1 +) +if not "%~nx1" == "rt.jar" ( + @echo File '%1' is not rt.jar + exit /b 1 +) +if not exist %1 ( + @echo %1 not found. + exit /b 1 +) + +if exist "%BUILDDIR%" rmdir /s /q "%BUILDDIR%" +mkdir "%BUILDDIR%" + +for /r "%SOURCEDIR%" %%j in (*.java) do ( + javac -O -d "%BUILDDIR%" "%%j" + if ERRORLEVEL 1 ( + @echo "Failed to compile %%j" + exit /b 1 + ) +) + +jar uvf %1 -C "%BUILDDIR%" . +if ERRORLEVEL 1 ( + @echo "Failed to update %1" + exit /b 1 +) + +@echo "Completed" diff --git a/tools/demobench/package/bugfixes/apply.sh b/tools/demobench/package/bugfixes/apply.sh new file mode 100755 index 0000000000..560f83abc7 --- /dev/null +++ b/tools/demobench/package/bugfixes/apply.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +BASEDIR=$(dirname $0) +SOURCEDIR=$BASEDIR/java +BUILDDIR=$BASEDIR/build +RTJAR=$1 + +if [ -z "$RTJAR" ]; then + echo "Need location of rt.jar" + exit 1 +elif [ $(basename $RTJAR) != "rt.jar" ]; then + echo "File is not rt.jar" + exit 1 +elif [ ! -f $RTJAR ]; then + echo "$RTJAR not found" + exit 1 +fi + +# Bugfixes: +# ========= +# +# sun.swing.JLightweightFrame:473 +# https://github.com/JetBrains/jdk8u_jdk/issues/6 +# https://github.com/JetBrains/jdk8u/issues/8 + +rm -rf $BUILDDIR && mkdir $BUILDDIR +if (javac -O -d $BUILDDIR $(find $SOURCEDIR -name "*.java")); then + chmod u+w $RTJAR + jar uvf $RTJAR -C $BUILDDIR . + chmod ugo-w $RTJAR +fi + diff --git a/tools/demobench/package/bugfixes/java/sun/swing/JLightweightFrame.java b/tools/demobench/package/bugfixes/java/sun/swing/JLightweightFrame.java new file mode 100644 index 0000000000..6f39eb5d62 --- /dev/null +++ b/tools/demobench/package/bugfixes/java/sun/swing/JLightweightFrame.java @@ -0,0 +1,515 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.swing; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.MouseInfo; +import java.awt.Point; +import java.awt.PointerInfo; +import java.awt.Rectangle; +import java.awt.Window; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragGestureRecognizer; +import java.awt.dnd.DragSource; +import java.awt.dnd.DropTarget; +import java.awt.dnd.InvalidDnDOperationException; +import java.awt.dnd.peer.DragSourceContextPeer; +import java.awt.event.ContainerEvent; +import java.awt.event.ContainerListener; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.security.AccessController; +import java.util.logging.Logger; +import javax.swing.JComponent; + +import javax.swing.JLayeredPane; +import javax.swing.JPanel; +import javax.swing.JRootPane; +import javax.swing.LayoutFocusTraversalPolicy; +import javax.swing.RepaintManager; +import javax.swing.RootPaneContainer; +import javax.swing.SwingUtilities; + +import sun.awt.DisplayChangedListener; +import sun.awt.LightweightFrame; +import sun.security.action.GetPropertyAction; +import sun.swing.SwingUtilities2.RepaintListener; + +/** + * The frame serves as a lightweight container which paints its content + * to an offscreen image and provides access to the image's data via the + * {@link LightweightContent} interface. Note, that it may not be shown + * as a standalone toplevel frame. Its purpose is to provide functionality + * for lightweight embedding. + * + * @author Artem Ananiev + * @author Anton Tarasov + */ +public final class JLightweightFrame extends LightweightFrame implements RootPaneContainer { + + private final JRootPane rootPane = new JRootPane(); + + private LightweightContent content; + + private Component component; + private JPanel contentPane; + + private BufferedImage bbImage; + + private volatile int scaleFactor = 1; + + /** + * {@code copyBufferEnabled}, true by default, defines the following strategy. + * A duplicating (copy) buffer is created for the original pixel buffer. + * The copy buffer is synchronized with the original buffer every time the + * latter changes. {@code JLightweightFrame} passes the copy buffer array + * to the {@link LightweightContent#imageBufferReset} method. The code spot + * which synchronizes two buffers becomes the only critical section guarded + * by the lock (managed with the {@link LightweightContent#paintLock()}, + * {@link LightweightContent#paintUnlock()} methods). + */ + private static boolean copyBufferEnabled; + private int[] copyBuffer; + + private PropertyChangeListener layoutSizeListener; + private RepaintListener repaintListener; + + static { + SwingAccessor.setJLightweightFrameAccessor(new SwingAccessor.JLightweightFrameAccessor() { + @Override + public void updateCursor(JLightweightFrame frame) { + frame.updateClientCursor(); + } + }); + copyBufferEnabled = "true".equals(AccessController. + doPrivileged(new GetPropertyAction("swing.jlf.copyBufferEnabled", "true"))); + } + + /** + * Constructs a new, initially invisible {@code JLightweightFrame} + * instance. + */ + public JLightweightFrame() { + super(); + copyBufferEnabled = "true".equals(AccessController. + doPrivileged(new GetPropertyAction("swing.jlf.copyBufferEnabled", "true"))); + + add(rootPane, BorderLayout.CENTER); + setFocusTraversalPolicy(new LayoutFocusTraversalPolicy()); + if (getGraphicsConfiguration().isTranslucencyCapable()) { + setBackground(new Color(0, 0, 0, 0)); + } + + layoutSizeListener = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent e) { + Dimension d = (Dimension)e.getNewValue(); + + if ("preferredSize".equals(e.getPropertyName())) { + content.preferredSizeChanged(d.width, d.height); + + } else if ("maximumSize".equals(e.getPropertyName())) { + content.maximumSizeChanged(d.width, d.height); + + } else if ("minimumSize".equals(e.getPropertyName())) { + content.minimumSizeChanged(d.width, d.height); + } + } + }; + + repaintListener = (JComponent c, int x, int y, int w, int h) -> { + Window jlf = SwingUtilities.getWindowAncestor(c); + if (jlf != JLightweightFrame.this) { + return; + } + Point p = SwingUtilities.convertPoint(c, x, y, jlf); + Rectangle r = new Rectangle(p.x, p.y, w, h).intersection( + new Rectangle(0, 0, bbImage.getWidth() / scaleFactor, + bbImage.getHeight() / scaleFactor)); + + if (!r.isEmpty()) { + notifyImageUpdated(r.x, r.y, r.width, r.height); + } + }; + + SwingAccessor.getRepaintManagerAccessor().addRepaintListener( + RepaintManager.currentManager(this), repaintListener); + } + + @Override + public void dispose() { + SwingAccessor.getRepaintManagerAccessor().removeRepaintListener( + RepaintManager.currentManager(this), repaintListener); + super.dispose(); + } + + /** + * Sets the {@link LightweightContent} instance for this frame. + * The {@code JComponent} object returned by the + * {@link LightweightContent#getComponent()} method is immediately + * added to the frame's content pane. + * + * @param content the {@link LightweightContent} instance + */ + public void setContent(final LightweightContent content) { + if (content == null) { + System.err.println("JLightweightFrame.setContent: content may not be null!"); + return; + } + this.content = content; + this.component = content.getComponent(); + + Dimension d = this.component.getPreferredSize(); + content.preferredSizeChanged(d.width, d.height); + + d = this.component.getMaximumSize(); + content.maximumSizeChanged(d.width, d.height); + + d = this.component.getMinimumSize(); + content.minimumSizeChanged(d.width, d.height); + + initInterior(); + } + + @Override + public Graphics getGraphics() { + if (bbImage == null) return null; + + Graphics2D g = bbImage.createGraphics(); + g.setBackground(getBackground()); + g.setColor(getForeground()); + g.setFont(getFont()); + g.scale(scaleFactor, scaleFactor); + return g; + } + + /** + * {@inheritDoc} + * + * @see LightweightContent#focusGrabbed() + */ + @Override + public void grabFocus() { + if (content != null) content.focusGrabbed(); + } + + /** + * {@inheritDoc} + * + * @see LightweightContent#focusUngrabbed() + */ + @Override + public void ungrabFocus() { + if (content != null) content.focusUngrabbed(); + } + + @Override + public int getScaleFactor() { + return scaleFactor; + } + + @Override + public void notifyDisplayChanged(final int scaleFactor) { + if (scaleFactor != this.scaleFactor) { + if (!copyBufferEnabled) content.paintLock(); + try { + if (bbImage != null) { + resizeBuffer(getWidth(), getHeight(), scaleFactor); + } + } finally { + if (!copyBufferEnabled) content.paintUnlock(); + } + this.scaleFactor = scaleFactor; + } + if (getPeer() instanceof DisplayChangedListener) { + ((DisplayChangedListener)getPeer()).displayChanged(); + } + repaint(); + } + + @Override + public void addNotify() { + super.addNotify(); + if (getPeer() instanceof DisplayChangedListener) { + ((DisplayChangedListener)getPeer()).displayChanged(); + } + } + + private void syncCopyBuffer(boolean reset, int x, int y, int w, int h, int scale) { + content.paintLock(); + try { + int[] srcBuffer = ((DataBufferInt)bbImage.getRaster().getDataBuffer()).getData(); + if (reset) { + copyBuffer = new int[srcBuffer.length]; + } + int linestride = bbImage.getWidth(); + + x *= scale; + y *= scale; + w *= scale; + h *= scale; + + for (int i=0; i= newW) && (oldH >= newH)) { + createBB = false; + } else { + if (oldW >= newW) { + newW = oldW; + } else { + newW = Math.max((int)(oldW * 1.2), width); + } + if (oldH >= newH) { + newH = oldH; + } else { + newH = Math.max((int)(oldH * 1.2), height); + } + } + } + } + } + if (createBB) { + resizeBuffer(newW, newH, scaleFactor); + return; + } + content.imageReshaped(0, 0, width, height); + + } finally { + if (!copyBufferEnabled) { + content.paintUnlock(); + } + } + } + + private void resizeBuffer(int width, int height, int newScaleFactor) { + bbImage = new BufferedImage(width*newScaleFactor,height*newScaleFactor, + BufferedImage.TYPE_INT_ARGB_PRE); + int[] pixels= ((DataBufferInt)bbImage.getRaster().getDataBuffer()).getData(); + if (copyBufferEnabled) { + syncCopyBuffer(true, 0, 0, width, height, newScaleFactor); + pixels = copyBuffer; + } + content.imageBufferReset(pixels, 0, 0, width, height, + width * newScaleFactor, newScaleFactor); + } + + @Override + public JRootPane getRootPane() { + return rootPane; + } + + @Override + public void setContentPane(Container contentPane) { + getRootPane().setContentPane(contentPane); + } + + @Override + public Container getContentPane() { + return getRootPane().getContentPane(); + } + + @Override + public void setLayeredPane(JLayeredPane layeredPane) { + getRootPane().setLayeredPane(layeredPane); + } + + @Override + public JLayeredPane getLayeredPane() { + return getRootPane().getLayeredPane(); + } + + @Override + public void setGlassPane(Component glassPane) { + getRootPane().setGlassPane(glassPane); + } + + @Override + public Component getGlassPane() { + return getRootPane().getGlassPane(); + } + + + /* + * Notifies client toolkit that it should change a cursor. + * + * Called from the peer via SwingAccessor, because the + * Component.updateCursorImmediately method is final + * and could not be overridden. + */ + private void updateClientCursor() { + PointerInfo pointerInfo = MouseInfo.getPointerInfo(); + /* + * BUGFIX not yet applied upstream! + */ + if (pointerInfo == null) { + Logger log = Logger.getLogger(getClass().getName()); + log.warning("BUGFIX - NPE avoided"); + return; + } + Point p = pointerInfo.getLocation(); + SwingUtilities.convertPointFromScreen(p, this); + Component target = SwingUtilities.getDeepestComponentAt(this, p.x, p.y); + if (target != null) { + content.setCursor(target.getCursor()); + } + } + + public T createDragGestureRecognizer( + Class abstractRecognizerClass, + DragSource ds, Component c, int srcActions, + DragGestureListener dgl) + { + return content == null ? null : content.createDragGestureRecognizer( + abstractRecognizerClass, ds, c, srcActions, dgl); + } + + public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException { + return content == null ? null : content.createDragSourceContextPeer(dge); + } + + public void addDropTarget(DropTarget dt) { + if (content == null) return; + content.addDropTarget(dt); + } + + public void removeDropTarget(DropTarget dt) { + if (content == null) return; + content.removeDropTarget(dt); + } +} + diff --git a/tools/demobench/package/linux/CordaDemoBench.spec b/tools/demobench/package/linux/CordaDemoBench.spec index d000f25216..b3d6c3f0e3 100644 --- a/tools/demobench/package/linux/CordaDemoBench.spec +++ b/tools/demobench/package/linux/CordaDemoBench.spec @@ -20,6 +20,8 @@ Autoreq: 0 %define __jar_repack %{nil} %define _javaHome %{getenv:JAVA_HOME} +%define _bugfixdir %{_sourcedir}/CordaDemoBench/app/bugfixes +%define _rtJar %{_sourcedir}/CordaDemoBench/runtime/lib/rt.jar %description Corda DemoBench @@ -29,6 +31,12 @@ Corda DemoBench %build %install +# Apply bugfixes to installed rt.jar +if [ -f %{_bugfixdir}/apply.sh ]; then + chmod ugo+x %{_bugfixdir}/apply.sh + %{_bugfixdir}/apply.sh %{_rtJar} + rm -rf %{_bugfixdir} +fi rm -rf %{buildroot} mkdir -p %{buildroot}/opt cp -r %{_sourcedir}/CordaDemoBench %{buildroot}/opt diff --git a/tools/demobench/package/macosx/Corda DemoBench-post-image.sh b/tools/demobench/package/macosx/Corda DemoBench-post-image.sh index 2d1c42fd2f..43a94041f4 100644 --- a/tools/demobench/package/macosx/Corda DemoBench-post-image.sh +++ b/tools/demobench/package/macosx/Corda DemoBench-post-image.sh @@ -13,14 +13,27 @@ function signApplication() { echo "**** Failed to re-sign the embedded JVM" return 1 fi + + # Resign the application because we've deleted the bugfixes directory. + if ! (codesign --force --sign "$IDENTITY" --preserve-metadata=identifier,entitlements,requirements --verbose "$APPDIR"); then + echo "*** Failed to resign DemoBench application" + return 1 + fi } # Switch to folder containing application. cd ../images/image-*/Corda\ DemoBench.app -INSTALL_HOME=Contents/PlugIns/Java.runtime/Contents/Home/jre/bin -if (mkdir -p $INSTALL_HOME); then - cp $JAVA_HOME/bin/java $INSTALL_HOME +JRE_HOME=Contents/PlugIns/Java.runtime/Contents/Home/jre +if (mkdir -p $JRE_HOME/bin); then + cp $JAVA_HOME/bin/java $JRE_HOME/bin +fi + +BUGFIX_HOME=Contents/Java/bugfixes +if [ -f $BUGFIX_HOME/apply.sh ]; then + chmod ugo+x $BUGFIX_HOME/apply.sh + $BUGFIX_HOME/apply.sh $JRE_HOME/lib/rt.jar + rm -rf $BUGFIX_HOME fi # Switch to image directory in order to sign it. diff --git a/tools/demobench/package/windows/Corda DemoBench-post-image.wsf b/tools/demobench/package/windows/Corda DemoBench-post-image.wsf index 6d54bde85f..54302824e1 100644 --- a/tools/demobench/package/windows/Corda DemoBench-post-image.wsf +++ b/tools/demobench/package/windows/Corda DemoBench-post-image.wsf @@ -4,13 +4,15 @@