Martin Kunz 5 years ago
parent
commit
a19cc70ac1

+ 23 - 0
assembly.xml

@@ -0,0 +1,23 @@
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
+    <id>sources</id>
+    <formats>
+        <format>tar.gz</format>
+    </formats>
+    <includeBaseDirectory>false</includeBaseDirectory>
+    <fileSets>
+        <fileSet>
+            <directory>.</directory>
+            <includes>
+                <include>pom.xml</include>
+                <include>assembly.xml</include>
+            </includes>
+            <useDefaultExcludes>true</useDefaultExcludes>
+        </fileSet>
+        <fileSet>
+            <directory>src</directory>
+            <useDefaultExcludes>true</useDefaultExcludes>
+        </fileSet>
+    </fileSets>
+</assembly>

+ 298 - 0
pom.xml

@@ -0,0 +1,298 @@
+<?xml version="1.0"?>
+<project
+		xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+	<modelVersion>4.0.0</modelVersion>
+
+	<groupId>at.acdp.opcur</groupId>
+	<artifactId>helloworld</artifactId>
+	<version>1.0-SNAPSHOT</version>
+	<name>Hello World</name>
+	<packaging>bundle</packaging>
+
+	<properties>
+
+		<!--********************************************************************-->
+		<!--   Note: Update this section with relevant meta data                -->
+		<!--         that comes along with your URCap                           -->
+		<!--********************************************************************-->
+		<!--******************* BEGINNING OF URCAP META DATA *******************-->
+		<urcap.symbolicname>at.acdp.opcur</urcap.symbolicname>
+		<urcap.vendor>Universal Robots</urcap.vendor>
+		<urcap.contactAddress>Energivej 25, 5260 Odense S, Denmark</urcap.contactAddress>
+		<urcap.copyright>Copyright (C) 2009-2017 Universal Robots. All Rights Reserved</urcap.copyright>
+		<urcap.description>Hello World sample URCap</urcap.description>
+		<urcap.licenseType>Sample license</urcap.licenseType>
+		<!--********************** END OF URCAP META DATA **********************-->
+		<!--********************************************************************-->
+
+		<!-- Host, username and password of the robot to be used when running "mvn install -Premote" -->
+		<urcap.install.host></urcap.install.host>
+		<urcap.install.username>root</urcap.install.username>
+		<urcap.install.password>easybot</urcap.install.password>
+
+		<!--Install path for the UR Sim-->
+		<ursim.home></ursim.home>
+
+		<!--Host and standard user/password for UR Sim running in a VM-->
+		<ursimvm.install.host></ursimvm.install.host>
+		<ursimvm.install.username>ur</ursimvm.install.username>
+		<ursimvm.install.password>easybot</ursimvm.install.password>
+	</properties>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>3.6.0</version>
+				<configuration>
+					<source>1.6</source>
+					<target>1.6</target>
+				</configuration>
+			</plugin>
+			<plugin>
+				<artifactId>maven-resources-plugin</artifactId>
+				<version>3.0.2</version>
+			</plugin>
+			<plugin>
+				<artifactId>maven-jar-plugin</artifactId>
+				<configuration>
+					<archive>
+						<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+					</archive>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.felix</groupId>
+				<artifactId>maven-bundle-plugin</artifactId>
+				<version>2.4.0</version>
+				<extensions>true</extensions>
+				<executions>
+					<execution>
+						<id>bundle-manifest</id>
+						<phase>process-classes</phase>
+						<goals>
+							<goal>manifest</goal>
+						</goals>
+					</execution>
+				</executions>
+				<configuration>
+					<instructions>
+						<!--********** DO NOT MODIFY THE ENTRIES OF THIS SECTION **********-->
+						<Bundle-Category>URCap</Bundle-Category>
+						<Bundle-Activator>at.acdp.opcur.Activator</Bundle-Activator>
+						<Bundle-Vendor>${urcap.vendor}</Bundle-Vendor>
+						<Bundle-ContactAddress>${urcap.contactAddress}</Bundle-ContactAddress>
+						<Bundle-Copyright>${urcap.copyright}</Bundle-Copyright>
+						<Bundle-LicenseType>${urcap.licenseType}</Bundle-LicenseType>
+						<Bundle-Description>${urcap.description}</Bundle-Description>
+						<!--***************************************************************-->
+						<Import-Package>
+							com.ur.urcap.api*;version="[1.0.0,2.0.0)",
+							*
+						</Import-Package>
+					</instructions>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.codehaus.mojo</groupId>
+				<artifactId>exec-maven-plugin</artifactId>
+				<version>1.1</version>
+				<executions>
+					<!-- generate URCap package after compiling -->
+					<execution>
+						<id>package-urcap</id>
+						<phase>package</phase>
+						<goals>
+							<goal>exec</goal>
+						</goals>
+						<configuration>
+							<executable>cp</executable>
+							<commandlineArgs>target/${project.build.finalName}.jar target/${project.build.finalName}.urcap</commandlineArgs>
+							<workingDirectory>.</workingDirectory>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<artifactId>maven-assembly-plugin</artifactId>
+				<configuration>
+					<descriptors>
+						<descriptor>assembly.xml</descriptor>
+					</descriptors>
+				</configuration>
+				<executions>
+					<execution>
+						<phase>package</phase>
+						<goals>
+							<goal>single</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+	<dependencies>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+			<version>1.7.25</version>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-simple</artifactId>
+			<version>1.7.25</version>
+		</dependency>
+		<dependency>
+			<groupId>org.opcfoundation.ua</groupId>
+			<artifactId>opc-ua-stack</artifactId>
+			<version>1.3.345-SNAPSHOT</version>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.core</artifactId>
+			<version>4.3.0</version>
+		</dependency>
+
+		<dependency>
+			<groupId>com.ur.urcap</groupId>
+			<artifactId>api</artifactId>
+			<version>1.0.0.30</version>
+			<scope>provided</scope>
+		</dependency>
+
+		<!-- test dependencies -->
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.12</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.httpcomponents</groupId>
+			<artifactId>httpclient</artifactId>
+			<version>4.5.5</version>
+		</dependency>
+	</dependencies>
+
+	<profiles>
+		<profile>
+			<id>remote</id>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.codehaus.mojo</groupId>
+						<artifactId>exec-maven-plugin</artifactId>
+						<version>1.1</version>
+						<executions>
+							<execution>
+								<id>remote-install-urcap</id>
+								<phase>install</phase>
+								<goals>
+									<goal>exec</goal>
+								</goals>
+								<configuration>
+									<executable>sshpass</executable>
+									<commandlineArgs>-p ${urcap.install.password} scp -o StrictHostKeyChecking=no target/${project.build.finalName}.jar ${urcap.install.username}@${urcap.install.host}:/root/.urcaps/${urcap.symbolicname}.jar</commandlineArgs>
+									<workingDirectory>.</workingDirectory>
+								</configuration>
+							</execution>
+							<execution>
+								<id>remote-restart-ui</id>
+								<phase>install</phase>
+								<goals>
+									<goal>exec</goal>
+								</goals>
+								<configuration>
+									<executable>sshpass</executable>
+									<commandlineArgs>-p ${urcap.install.password} ssh ${urcap.install.username}@${urcap.install.host} pkill java</commandlineArgs>
+									<workingDirectory>.</workingDirectory>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+		<profile>
+			<id>local</id>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.codehaus.mojo</groupId>
+						<artifactId>exec-maven-plugin</artifactId>
+						<version>1.1</version>
+						<executions>
+							<execution>
+								<id>local-install-urcap</id>
+								<phase>install</phase>
+								<goals>
+									<goal>exec</goal>
+								</goals>
+								<configuration>
+									<executable>cp</executable>
+									<commandlineArgs>target/${project.build.finalName}.jar ${user.home}/.urcaps/${urcap.symbolicname}.jar</commandlineArgs>
+									<workingDirectory>.</workingDirectory>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+		<profile>
+			<id>ursim</id>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.codehaus.mojo</groupId>
+						<artifactId>exec-maven-plugin</artifactId>
+						<version>1.1</version>
+						<executions>
+							<execution>
+								<id>ursim-install-urcap</id>
+								<phase>install</phase>
+								<goals>
+									<goal>exec</goal>
+								</goals>
+								<configuration>
+									<executable>cp</executable>
+									<commandlineArgs>target/${project.build.finalName}.jar ${ursim.home}/.urcaps/${urcap.symbolicname}.jar</commandlineArgs>
+									<workingDirectory>.</workingDirectory>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+		<profile>
+			<id>ursimvm</id>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.codehaus.mojo</groupId>
+						<artifactId>exec-maven-plugin</artifactId>
+						<version>1.1</version>
+						<executions>
+							<execution>
+								<id>ursimvm-install-urcap</id>
+								<phase>install</phase>
+								<goals>
+									<goal>exec</goal>
+								</goals>
+								<configuration>
+									<executable>sshpass</executable>
+									<commandlineArgs>-p ${ursimvm.install.password} scp -o StrictHostKeyChecking=no target/${project.build.finalName}.jar ${ursimvm.install.username}@${ursimvm.install.host}:/home/ur/ursim-current/.urcaps/${urcap.symbolicname}.jar</commandlineArgs>
+									<workingDirectory>.</workingDirectory>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+	</profiles>
+</project>

+ 20 - 0
src/main/java/at/acdp/opcur/Activator.java

@@ -0,0 +1,20 @@
+package at.acdp.opcur;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import com.ur.urcap.api.contribution.InstallationNodeService;
+import com.ur.urcap.api.contribution.ProgramNodeService;
+
+public class Activator implements BundleActivator {
+	@Override
+	public void start(final BundleContext context) throws Exception {
+		HelloWorldInstallationNodeService helloWorldInstallationNodeService = new HelloWorldInstallationNodeService();
+
+		context.registerService(InstallationNodeService.class, helloWorldInstallationNodeService, null);
+		context.registerService(ProgramNodeService.class, new HelloWorldProgramNodeService(), null);
+	}
+
+	@Override
+	public void stop(BundleContext context) throws Exception {
+	}
+}

+ 293 - 0
src/main/java/at/acdp/opcur/ExampleKeys.java

@@ -0,0 +1,293 @@
+package at.acdp.opcur;
+/* ========================================================================
+ * Copyright (c) 2005-2015 The OPC Foundation, Inc. All rights reserved.
+ *
+ * OPC Foundation MIT License 1.00
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The complete license agreement can be found here:
+ * http://opcfoundation.org/License/MIT/1.00/
+ * ======================================================================*/
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.InvalidParameterSpecException;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+
+import org.opcfoundation.ua.common.ServiceResultException;
+import org.opcfoundation.ua.transport.security.Cert;
+import org.opcfoundation.ua.transport.security.KeyPair;
+import org.opcfoundation.ua.transport.security.PrivKey;
+import org.opcfoundation.ua.utils.CertificateUtils;
+import org.opcfoundation.ua.utils.CryptoUtil;
+
+/**
+ * Keys for examples
+ * Keystore.p12 contains 20 RSA keypairs with the following aliases
+ *
+ * alias               dname
+ *
+ * server_8192         CN=server
+ * server_4096         CN=server
+ * server_2048         CN=server
+ * server_1024         CN=server
+ * server_512          CN=server
+ *
+ * client_8192         CN=client
+ * client_4096         CN=client
+ * client_2048         CN=client
+ * client_1024         CN=client
+ * client_512          CN=client
+ *
+ * https_server_8192   CN=https_server
+ * https_server_4096   CN=https_server
+ * https_server_2048   CN=https_server
+ * https_server_1024   CN=https_server
+ * https_server_512    CN=https_server
+ *
+ * https_client_8192   CN=https_client
+ * https_client_4096   CN=https_client
+ * https_client_2048   CN=https_client
+ * https_client_1024   CN=https_client
+ * https_client_512    CN=https_client
+ *
+ * Keystore password is "password".
+ * Private key passwords are "password".
+ *
+ */
+public class ExampleKeys {
+
+    private static final String PRIVKEY_PASSWORD = "Opc.Ua";
+
+    /**
+     * Load file certificate and private key from applicationName.der & .pfx - or create ones if they do not exist
+     * @return the KeyPair composed of the certificate and private key
+     * @throws ServiceResultException
+     */
+    public static KeyPair getCert(String applicationName)
+            throws ServiceResultException
+    {
+        File certFile = new File(applicationName + ".der");
+        File privKeyFile =  new File(applicationName+ ".pem");
+        try {
+            Cert myCertificate = Cert.load( certFile );
+            PrivKey myPrivateKey = PrivKey.load( privKeyFile, PRIVKEY_PASSWORD );
+            return new KeyPair(myCertificate, myPrivateKey);
+        } catch (CertificateException e) {
+            throw new ServiceResultException( e );
+        } catch (IOException e) {
+            try {
+                String hostName = InetAddress.getLocalHost().getHostName();
+                String applicationUri = "urn:"+hostName+":"+applicationName;
+                KeyPair keys = CertificateUtils.createApplicationInstanceCertificate(applicationName, null, applicationUri, 3650, hostName);
+                keys.getCertificate().save(certFile);
+                keys.getPrivateKey().save(privKeyFile);
+                return keys;
+            } catch (Exception e1) {
+                throw new ServiceResultException( e1 );
+            }
+        } catch (NoSuchAlgorithmException e) {
+            throw new ServiceResultException( e );
+        } catch (InvalidKeyException e) {
+            throw new ServiceResultException( e );
+        } catch (InvalidKeySpecException e) {
+            throw new ServiceResultException( e );
+        } catch (NoSuchPaddingException e) {
+            throw new ServiceResultException( e );
+        } catch (InvalidAlgorithmParameterException e) {
+            throw new ServiceResultException( e );
+        } catch (IllegalBlockSizeException e) {
+            throw new ServiceResultException( e );
+        } catch (BadPaddingException e) {
+            throw new ServiceResultException( e );
+        } catch (InvalidParameterSpecException e) {
+            throw new ServiceResultException( e );
+        }
+    }
+
+    /**
+     * Load CA certificate and private key from SampleCA.der & .pfx - or create ones if they do not exist
+     * @return the KeyPair composed of the certificate and private key
+     * @throws ServiceResultException
+     */
+    public static KeyPair getCACert()
+            throws ServiceResultException
+    {
+        File certFile = new File("SampleCA.der");
+        File privKeyFile =  new File("SampleCA.pem");
+        try {
+            Cert myCertificate = Cert.load( certFile );
+            PrivKey myPrivateKey = PrivKey.load( privKeyFile, PRIVKEY_PASSWORD );
+            return new KeyPair(myCertificate, myPrivateKey);
+        } catch (CertificateException e) {
+            throw new ServiceResultException( e );
+        } catch (IOException e) {
+            try {
+                KeyPair keys = CertificateUtils.createIssuerCertificate("SampleCA", 3650, null);
+                keys.getCertificate().save(certFile);
+                keys.getPrivateKey().save(privKeyFile, PRIVKEY_PASSWORD);
+                return keys;
+            } catch (Exception e1) {
+                throw new ServiceResultException( e1 );
+            }
+        } catch (NoSuchAlgorithmException e) {
+            throw new ServiceResultException( e );
+        } catch (InvalidKeyException e) {
+            throw new ServiceResultException( e );
+        } catch (InvalidKeySpecException e) {
+            throw new ServiceResultException( e );
+        } catch (NoSuchPaddingException e) {
+            throw new ServiceResultException( e );
+        } catch (InvalidAlgorithmParameterException e) {
+            throw new ServiceResultException( e );
+        } catch (IllegalBlockSizeException e) {
+            throw new ServiceResultException( e );
+        } catch (BadPaddingException e) {
+            throw new ServiceResultException( e );
+        } catch (InvalidParameterSpecException e) {
+            throw new ServiceResultException( e );
+        }
+    }
+    /**
+     * Load file certificate and private key from applicationName.der & .pfx - or create ones if they do not exist
+     * @param applicationName
+     * @return the KeyPair composed of the certificate and private key
+     * @throws ServiceResultException
+     */
+    public static KeyPair getHttpsCert(String applicationName)
+            throws ServiceResultException
+    {
+        File certFile = new File(applicationName + "_https.der");
+        File privKeyFile =  new File(applicationName+ "_https.pem");
+        try {
+            Cert myCertificate = Cert.load( certFile );
+            PrivKey myPrivateKey = PrivKey.load( privKeyFile, PRIVKEY_PASSWORD );
+            return new KeyPair(myCertificate, myPrivateKey);
+        } catch (CertificateException e) {
+            throw new ServiceResultException( e );
+        } catch (IOException e) {
+            try {
+                KeyPair caCert = getCACert();
+                String hostName = InetAddress.getLocalHost().getHostName();
+                String applicationUri = "urn:"+hostName+":"+applicationName;
+                KeyPair keys = CertificateUtils.createHttpsCertificate(hostName, applicationUri, 3650, caCert);
+                keys.getCertificate().save(certFile);
+                keys.getPrivateKey().save(privKeyFile, PRIVKEY_PASSWORD);
+                return keys;
+            } catch (Exception e1) {
+                throw new ServiceResultException( e1 );
+            }
+        } catch (NoSuchAlgorithmException e) {
+            throw new ServiceResultException( e );
+        } catch (InvalidKeyException e) {
+            throw new ServiceResultException( e );
+        } catch (InvalidKeySpecException e) {
+            throw new ServiceResultException( e );
+        } catch (NoSuchPaddingException e) {
+            throw new ServiceResultException( e );
+        } catch (InvalidAlgorithmParameterException e) {
+            throw new ServiceResultException( e );
+        } catch (IllegalBlockSizeException e) {
+            throw new ServiceResultException( e );
+        } catch (BadPaddingException e) {
+            throw new ServiceResultException( e );
+        } catch (InvalidParameterSpecException e) {
+            throw new ServiceResultException( e );
+        }
+    }
+    /**
+     * Open keypair from keystore.p12 used in some of these examples.
+     *
+     * Usable aliases are : "server", "client", "https_server", "https_client"
+     * Usable keysizes are : 8192, 4096, 2048, 1024
+     *
+     * @param alias
+     * @param keysize
+     * @return
+     * @throws KeyStoreException
+     * @throws IOException
+     * @throws CertificateException
+     * @throws NoSuchAlgorithmException
+     * @throws UnrecoverableKeyException
+     */
+    public static KeyPair getKeyPair(String alias, int keysize) throws ServiceResultException {
+        try {
+            Certificate cert = ks.getCertificate(alias+"_"+keysize);
+            Key key = ks.getKey(alias+"_"+keysize, "password".toCharArray());
+            KeyPair pair = new KeyPair( new Cert( (X509Certificate) cert ), new PrivKey( (RSAPrivateKey) key ) );
+            return pair;
+        } catch (KeyStoreException e) {
+            throw new ServiceResultException( e );
+        } catch (UnrecoverableKeyException e) {
+            throw new ServiceResultException( e );
+        } catch (NoSuchAlgorithmException e) {
+            throw new ServiceResultException( e );
+        } catch (CertificateEncodingException e) {
+            throw new ServiceResultException( e );
+        }
+    }
+
+    static KeyStore ks;
+
+    static {
+        try {
+            ks = KeyStore.getInstance("pkcs12");
+            InputStream is = ExampleKeys.class.getResourceAsStream("keystore.p12");
+            try {
+                ks.load( is, "password".toCharArray() );
+            } catch (NoSuchAlgorithmException e) {
+                throw new RuntimeException(e);
+            } catch (CertificateException e) {
+                throw new RuntimeException(e);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            } finally {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                }
+            }
+        } catch (KeyStoreException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+
+}

+ 65 - 0
src/main/java/at/acdp/opcur/HelloWorldInstallationNodeContribution.java

@@ -0,0 +1,65 @@
+package at.acdp.opcur;
+
+import com.ur.urcap.api.contribution.InstallationNodeContribution;
+import com.ur.urcap.api.domain.data.DataModel;
+import com.ur.urcap.api.domain.script.ScriptWriter;
+import com.ur.urcap.api.ui.annotation.Input;
+import com.ur.urcap.api.ui.component.InputEvent;
+import com.ur.urcap.api.ui.component.InputTextField;
+
+public class HelloWorldInstallationNodeContribution implements InstallationNodeContribution {
+
+	private static final String POPUPTITLE_KEY = "popuptitle";
+	private static final String DEFAULT_VALUE = "Hello World";
+
+	private DataModel model;
+
+	public HelloWorldInstallationNodeContribution(DataModel model) {
+		this.model = model;
+	}
+
+	@Input(id = POPUPTITLE_KEY)
+	private InputTextField popupTitleField;
+
+	@Input(id = POPUPTITLE_KEY)
+	public void onMessageChange(InputEvent event) {
+		if (event.getEventType() == InputEvent.EventType.ON_CHANGE) {
+			setPopupTitle(popupTitleField.getText());
+		}
+	}
+
+	@Override
+	public void openView() {
+		popupTitleField.setText(getPopupTitle());
+	}
+
+	@Override
+	public void closeView() { }
+
+	public boolean isDefined() {
+		return !getPopupTitle().isEmpty();
+	}
+
+	@Override
+	public void generateScript(ScriptWriter writer) {
+		// Store the popup title in a global variable so it is globally available to all HelloWorld program nodes.
+		writer.assign("hello_world_popup_title", "\"" + getPopupTitle() + "\"");
+	}
+
+	public String getPopupTitle() {
+		return model.get(POPUPTITLE_KEY, DEFAULT_VALUE);
+	}
+
+	private void setPopupTitle(String message) {
+		if ("".equals(message)) {
+			resetToDefaultValue();
+		} else {
+			model.set(POPUPTITLE_KEY, message);
+		}
+	}
+
+	private void resetToDefaultValue() {
+		popupTitleField.setText(DEFAULT_VALUE);
+		model.set(POPUPTITLE_KEY, DEFAULT_VALUE);
+	}
+}

+ 30 - 0
src/main/java/at/acdp/opcur/HelloWorldInstallationNodeService.java

@@ -0,0 +1,30 @@
+package at.acdp.opcur;
+
+import com.ur.urcap.api.contribution.InstallationNodeContribution;
+import com.ur.urcap.api.contribution.InstallationNodeService;
+import com.ur.urcap.api.domain.URCapAPI;
+
+import java.io.InputStream;
+
+import com.ur.urcap.api.domain.data.DataModel;
+
+public class HelloWorldInstallationNodeService implements InstallationNodeService {
+
+	public HelloWorldInstallationNodeService() { }
+
+	@Override
+	public InstallationNodeContribution createInstallationNode(URCapAPI api, DataModel model) {
+		return new HelloWorldInstallationNodeContribution(model);
+	}
+
+	@Override
+	public String getTitle() {
+		return "Hello World";
+	}
+
+	@Override
+	public InputStream getHTML() {
+		InputStream is = this.getClass().getResourceAsStream("/at.acdp.opcur/installation.html");
+		return is;
+	}
+}

+ 93 - 0
src/main/java/at/acdp/opcur/HelloWorldProgramNodeContribution.java

@@ -0,0 +1,93 @@
+package at.acdp.opcur;
+
+import com.ur.urcap.api.contribution.ProgramNodeContribution;
+import com.ur.urcap.api.domain.URCapAPI;
+import com.ur.urcap.api.domain.data.DataModel;
+import com.ur.urcap.api.domain.script.ScriptWriter;
+import com.ur.urcap.api.ui.annotation.Input;
+import com.ur.urcap.api.ui.annotation.Label;
+import com.ur.urcap.api.ui.component.InputEvent;
+import com.ur.urcap.api.ui.component.InputTextField;
+import com.ur.urcap.api.ui.component.LabelComponent;
+
+public class HelloWorldProgramNodeContribution implements ProgramNodeContribution {
+	private static final String NAME = "name";
+
+	private final DataModel model;
+	private final URCapAPI api;
+
+	public HelloWorldProgramNodeContribution(URCapAPI api, DataModel model) {
+		this.api = api;
+		this.model = model;
+	}
+
+	@Input(id = "yourname")
+	private InputTextField nameTextField;
+
+	@Label(id = "titlePreviewLabel")
+	private LabelComponent titlePreviewLabel;
+
+	@Label(id = "messagePreviewLabel")
+	private LabelComponent messagePreviewLabel;
+
+	@Input(id = "yourname")
+	public void onInput(InputEvent event) {
+		if (event.getEventType() == InputEvent.EventType.ON_CHANGE) {
+			setName(nameTextField.getText());
+			updatePopupMessageAndPreview();
+		}
+	}
+
+	@Override
+	public void openView() {
+		nameTextField.setText(getName());
+		updatePopupMessageAndPreview();
+	}
+
+	@Override
+	public void closeView() {
+	}
+
+	@Override
+	public String getTitle() {
+		return "Hello World: " + (model.isSet(NAME) ? getName() : "");
+	}
+
+	@Override
+	public boolean isDefined() {
+		return getInstallation().isDefined() && !getName().isEmpty();
+	}
+
+	@Override
+	public void generateScript(ScriptWriter writer) {
+		// Directly generate this Program Node's popup message + access the popup title through a global variable
+		writer.appendLine("popup(\"" + generatePopupMessage() + "\", hello_world_popup_title, False, False, blocking=True)");
+		writer.writeChildren();
+	}
+
+	private String generatePopupMessage() {
+		return model.isSet(NAME) ? "Hello " + getName() + ", welcome to PolyScope!" : "No name set";
+	}
+
+	private void updatePopupMessageAndPreview() {
+		messagePreviewLabel.setText(generatePopupMessage());
+		titlePreviewLabel.setText(getInstallation().isDefined() ? getInstallation().getPopupTitle() : "No title set");
+	}
+
+	private String getName() {
+		return model.get(NAME, "");
+	}
+
+	private void setName(String name) {
+		if ("".equals(name)){
+			model.remove(NAME);
+		}else{
+			model.set(NAME, name);
+		}
+	}
+
+	private HelloWorldInstallationNodeContribution getInstallation() {
+		return api.getInstallationNode(HelloWorldInstallationNodeContribution.class);
+	}
+
+}

+ 45 - 0
src/main/java/at/acdp/opcur/HelloWorldProgramNodeService.java

@@ -0,0 +1,45 @@
+package at.acdp.opcur;
+
+import com.ur.urcap.api.contribution.ProgramNodeContribution;
+import com.ur.urcap.api.contribution.ProgramNodeService;
+import com.ur.urcap.api.domain.URCapAPI;
+import com.ur.urcap.api.domain.data.DataModel;
+
+import java.io.InputStream;
+
+public class HelloWorldProgramNodeService implements ProgramNodeService {
+
+	public HelloWorldProgramNodeService() {
+	}
+
+	@Override
+	public String getId() {
+		return "HelloWorldNode";
+	}
+
+	@Override
+	public String getTitle() {
+		return "Hello World";
+	}
+
+	@Override
+	public InputStream getHTML() {
+		InputStream is = this.getClass().getResourceAsStream("/at.acdp.opcur/programnode.html");
+		return is;
+	}
+
+	@Override
+	public boolean isDeprecated() {
+		return false;
+	}
+
+	@Override
+	public boolean isChildrenAllowed() {
+		return true;
+	}
+
+	@Override
+	public ProgramNodeContribution createNode(URCapAPI api, DataModel model) {
+		return new HelloWorldProgramNodeContribution(api, model);
+	}
+}

+ 395 - 0
src/main/java/at/acdp/opcur/OPCTest.java

@@ -0,0 +1,395 @@
+package at.acdp.opcur;
+/*
+ * ======================================================================== Copyright (c) 2005-2015
+ * The OPC Foundation, Inc. All rights reserved.
+ *
+ * OPC Foundation MIT License 1.00
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+ * associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * The complete license agreement can be found here: http://opcfoundation.org/License/MIT/1.00/
+ * ======================================================================
+ */
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.Security;
+import java.security.interfaces.RSAPrivateKey;
+import java.util.Random;
+import java.util.UUID;
+
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.opcfoundation.ua.application.Application;
+import org.opcfoundation.ua.application.Server;
+import org.opcfoundation.ua.builtintypes.ByteString;
+import org.opcfoundation.ua.builtintypes.DataValue;
+import org.opcfoundation.ua.builtintypes.LocalizedText;
+import org.opcfoundation.ua.builtintypes.NodeId;
+import org.opcfoundation.ua.builtintypes.QualifiedName;
+import org.opcfoundation.ua.builtintypes.StatusCode;
+import org.opcfoundation.ua.builtintypes.UnsignedInteger;
+import org.opcfoundation.ua.builtintypes.Variant;
+import org.opcfoundation.ua.common.ServiceFaultException;
+import org.opcfoundation.ua.common.ServiceResultException;
+import org.opcfoundation.ua.core.ActivateSessionRequest;
+import org.opcfoundation.ua.core.ActivateSessionResponse;
+import org.opcfoundation.ua.core.AddNodesRequest;
+import org.opcfoundation.ua.core.AddNodesResponse;
+import org.opcfoundation.ua.core.AddReferencesRequest;
+import org.opcfoundation.ua.core.AddReferencesResponse;
+import org.opcfoundation.ua.core.AttributeServiceSetHandler;
+import org.opcfoundation.ua.core.Attributes;
+import org.opcfoundation.ua.core.BrowseNextRequest;
+import org.opcfoundation.ua.core.BrowseNextResponse;
+import org.opcfoundation.ua.core.BrowseRequest;
+import org.opcfoundation.ua.core.BrowseResponse;
+import org.opcfoundation.ua.core.BrowseResult;
+import org.opcfoundation.ua.core.CancelRequest;
+import org.opcfoundation.ua.core.CancelResponse;
+import org.opcfoundation.ua.core.CloseSessionRequest;
+import org.opcfoundation.ua.core.CloseSessionResponse;
+import org.opcfoundation.ua.core.CreateSessionRequest;
+import org.opcfoundation.ua.core.CreateSessionResponse;
+import org.opcfoundation.ua.core.DeleteNodesRequest;
+import org.opcfoundation.ua.core.DeleteNodesResponse;
+import org.opcfoundation.ua.core.DeleteReferencesRequest;
+import org.opcfoundation.ua.core.DeleteReferencesResponse;
+import org.opcfoundation.ua.core.EndpointConfiguration;
+import org.opcfoundation.ua.core.HistoryReadRequest;
+import org.opcfoundation.ua.core.HistoryReadResponse;
+import org.opcfoundation.ua.core.HistoryUpdateRequest;
+import org.opcfoundation.ua.core.HistoryUpdateResponse;
+import org.opcfoundation.ua.core.Identifiers;
+import org.opcfoundation.ua.core.NodeManagementServiceSetHandler;
+import org.opcfoundation.ua.core.QueryFirstRequest;
+import org.opcfoundation.ua.core.QueryFirstResponse;
+import org.opcfoundation.ua.core.QueryNextRequest;
+import org.opcfoundation.ua.core.QueryNextResponse;
+import org.opcfoundation.ua.core.ReadRequest;
+import org.opcfoundation.ua.core.ReadResponse;
+import org.opcfoundation.ua.core.ReadValueId;
+import org.opcfoundation.ua.core.RegisterNodesRequest;
+import org.opcfoundation.ua.core.RegisterNodesResponse;
+import org.opcfoundation.ua.core.ServiceFault;
+import org.opcfoundation.ua.core.SessionServiceSetHandler;
+import org.opcfoundation.ua.core.SignatureData;
+import org.opcfoundation.ua.core.StatusCodes;
+import org.opcfoundation.ua.core.TranslateBrowsePathsToNodeIdsRequest;
+import org.opcfoundation.ua.core.TranslateBrowsePathsToNodeIdsResponse;
+import org.opcfoundation.ua.core.UnregisterNodesRequest;
+import org.opcfoundation.ua.core.UnregisterNodesResponse;
+import org.opcfoundation.ua.core.UserTokenPolicy;
+import org.opcfoundation.ua.core.WriteRequest;
+import org.opcfoundation.ua.core.WriteResponse;
+import org.opcfoundation.ua.transport.endpoint.EndpointServiceRequest;
+import org.opcfoundation.ua.transport.security.CertificateValidator;
+import org.opcfoundation.ua.transport.security.HttpsSecurityPolicy;
+import org.opcfoundation.ua.transport.security.KeyPair;
+import org.opcfoundation.ua.transport.security.SecurityAlgorithm;
+import org.opcfoundation.ua.transport.security.SecurityMode;
+import org.opcfoundation.ua.transport.security.SecurityPolicy;
+import org.opcfoundation.ua.utils.CryptoUtil;
+import org.opcfoundation.ua.utils.EndpointUtil;
+
+
+/**
+ * Simple Server example. This server responds to stack test and endpoint discover service requests.
+ *
+ */
+public class OPCTest {
+
+
+    static class MyAttributeServiceHandler implements AttributeServiceSetHandler {
+
+        @Override
+        public void onHistoryRead(EndpointServiceRequest<HistoryReadRequest, HistoryReadResponse> req)
+                throws ServiceFaultException {
+
+        }
+
+        @Override
+        public void onHistoryUpdate(EndpointServiceRequest<HistoryUpdateRequest, HistoryUpdateResponse> req)
+                throws ServiceFaultException {
+
+        }
+
+        @Override
+        public void onRead(EndpointServiceRequest<ReadRequest, ReadResponse> req) throws ServiceFaultException {
+            ReadRequest request = req.getRequest();
+            ReadValueId[] nodesToRead = request.getNodesToRead();
+
+            DataValue[] results = new DataValue[nodesToRead.length];
+            for (int i = 0; i < nodesToRead.length; i++) {
+                if (Identifiers.RootFolder.equals(nodesToRead[i].getNodeId())) {
+                    if (Attributes.BrowseName.equals(nodesToRead[i].getAttributeId())) {
+                        results[i] = new DataValue(new Variant(new QualifiedName("Root")));
+                    } else if (Attributes.DisplayName.equals(nodesToRead[i].getAttributeId())) {
+                        results[i] = new DataValue(new Variant(new LocalizedText("Root", LocalizedText.NO_LOCALE)));
+                    } else {
+                        results[i] = new DataValue(new StatusCode(StatusCodes.Bad_AttributeIdInvalid));
+                    }
+                } else {
+                    results[i] = new DataValue(new StatusCode(StatusCodes.Bad_NodeIdUnknown));
+                }
+            }
+            ReadResponse response = new ReadResponse(null, results, null);
+            req.sendResponse(response);
+        }
+
+        @Override
+        public void onWrite(EndpointServiceRequest<WriteRequest, WriteResponse> req) throws ServiceFaultException {
+
+        }
+
+    };
+
+    static class MyNodeManagementServiceHandler implements NodeManagementServiceSetHandler {
+
+        @Override
+        public void onAddNodes(EndpointServiceRequest<AddNodesRequest, AddNodesResponse> req) throws ServiceFaultException {
+
+        }
+
+        @Override
+        public void onAddReferences(EndpointServiceRequest<AddReferencesRequest, AddReferencesResponse> req)
+                throws ServiceFaultException {
+
+        }
+
+        @Override
+        public void onBrowse(EndpointServiceRequest<BrowseRequest, BrowseResponse> req) throws ServiceFaultException {
+            BrowseRequest request = req.getRequest();
+            BrowseResult[] Results = new BrowseResult[request.getNodesToBrowse().length];
+            for (int i = 0; i < request.getNodesToBrowse().length; i++) {
+                StatusCode statusCode;
+                if (Identifiers.RootFolder.equals(request.getNodesToBrowse()[i].getNodeId())) {
+                    statusCode = StatusCode.GOOD;
+                } else {
+                    statusCode = new StatusCode(StatusCodes.Bad_NodeIdUnknown);
+                }
+                Results[i] = new BrowseResult(statusCode, null, null);
+            }
+            BrowseResponse response = new BrowseResponse(null, Results, null);
+            req.sendResponse(response);
+
+        }
+
+        @Override
+        public void onBrowseNext(EndpointServiceRequest<BrowseNextRequest, BrowseNextResponse> req)
+                throws ServiceFaultException {
+
+        }
+
+        @Override
+        public void onDeleteNodes(EndpointServiceRequest<DeleteNodesRequest, DeleteNodesResponse> req)
+                throws ServiceFaultException {
+
+        }
+
+        @Override
+        public void onDeleteReferences(EndpointServiceRequest<DeleteReferencesRequest, DeleteReferencesResponse> req)
+                throws ServiceFaultException {
+
+        }
+
+        @Override
+        public void onQueryFirst(EndpointServiceRequest<QueryFirstRequest, QueryFirstResponse> req)
+                throws ServiceFaultException {
+
+        }
+
+        @Override
+        public void onQueryNext(EndpointServiceRequest<QueryNextRequest, QueryNextResponse> req)
+                throws ServiceFaultException {
+
+        }
+
+        @Override
+        public void onRegisterNodes(EndpointServiceRequest<RegisterNodesRequest, RegisterNodesResponse> req)
+                throws ServiceFaultException {
+
+        }
+
+        @Override
+        public void onTranslateBrowsePathsToNodeIds(
+                EndpointServiceRequest<TranslateBrowsePathsToNodeIdsRequest, TranslateBrowsePathsToNodeIdsResponse> req)
+                throws ServiceFaultException {
+
+        }
+
+        @Override
+        public void onUnregisterNodes(EndpointServiceRequest<UnregisterNodesRequest, UnregisterNodesResponse> req)
+                throws ServiceFaultException {
+
+        }
+
+    }
+
+
+    static class MyServerExample extends Server implements SessionServiceSetHandler {
+
+        public MyServerExample(Application application) throws Exception {
+            super(application);
+            addServiceHandler(this);
+
+            // Add Client Application Instance Certificate validator - Accept them all (for now)
+            application.getOpctcpSettings().setCertificateValidator(CertificateValidator.ALLOW_ALL);
+            application.getHttpsSettings().setCertificateValidator(CertificateValidator.ALLOW_ALL);
+
+            // The HTTPS SecurityPolicies are defined separate from the endpoint securities
+            application.getHttpsSettings().setHttpsSecurityPolicies(HttpsSecurityPolicy.ALL);
+
+            // Peer verifier
+            application.getHttpsSettings().setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+
+            // Load Servers's Application Instance Certificate...
+            KeyPair myServerApplicationInstanceCertificate = ExampleKeys.getCert("ServerExample1");
+            application.addApplicationInstanceCertificate(myServerApplicationInstanceCertificate);
+            // ...and HTTPS certificate
+            KeyPair myHttpsCertificate = ExampleKeys.getHttpsCert("ServerExample1");
+            application.getHttpsSettings().setKeyPair(myHttpsCertificate);
+
+            // Add User Token Policies
+            addUserTokenPolicy(UserTokenPolicy.ANONYMOUS);
+            addUserTokenPolicy(UserTokenPolicy.SECURE_USERNAME_PASSWORD);
+
+            // Create an endpoint for each network interface
+            String hostname = EndpointUtil.getHostname();
+            String bindAddress, endpointAddress;
+            for (String addr : EndpointUtil.getInetAddressNames()) {
+                bindAddress = "https://" + addr + ":8443/UAExample";
+                endpointAddress = "https://" + hostname + ":8443/UAExample";
+                System.out.println(endpointAddress + " bound at " + bindAddress);
+                // The HTTPS ports are using NONE OPC security
+                bind(bindAddress, endpointAddress, SecurityMode.NONE);
+
+                bindAddress = "opc.tcp://" + addr + ":8666/UAExample";
+                endpointAddress = "opc.tcp://" + hostname + ":8666/UAExample";
+                System.out.println(endpointAddress + " bound at " + bindAddress);
+                bind(bindAddress, endpointAddress, SecurityMode.ALL);
+            }
+
+            //////////////////////////////////////
+        }
+
+        @Override
+        public void onActivateSession(EndpointServiceRequest<ActivateSessionRequest, ActivateSessionResponse> msgExchange)
+                throws ServiceFaultException {
+            ActivateSessionResponse res = new ActivateSessionResponse();
+            res.setServerNonce(CryptoUtil.createNonce(32));
+            res.setResults(new StatusCode[] {StatusCode.GOOD});
+            msgExchange.sendResponse(res);
+        }
+
+        @Override
+        public void onCancel(EndpointServiceRequest<CancelRequest, CancelResponse> msgExchange)
+                throws ServiceFaultException {
+
+        }
+
+        @Override
+        public void onCloseSession(EndpointServiceRequest<CloseSessionRequest, CloseSessionResponse> msgExchange)
+                throws ServiceFaultException {
+            CloseSessionResponse res = new CloseSessionResponse();
+            msgExchange.sendResponse(res);
+        }
+
+        @Override
+        public void onCreateSession(EndpointServiceRequest<CreateSessionRequest, CreateSessionResponse> msgExchange)
+                throws ServiceFaultException {
+            CreateSessionRequest req = msgExchange.getRequest();
+            CreateSessionResponse res = new CreateSessionResponse();
+            byte[] token = new byte[32];
+            byte[] nonce = new byte[32];
+            Random r = new Random();
+            r.nextBytes(nonce);
+            r.nextBytes(token);
+            res.setAuthenticationToken(new NodeId(0, token));
+            EndpointConfiguration endpointConfiguration = EndpointConfiguration.defaults();
+            res.setMaxRequestMessageSize(UnsignedInteger
+                    .valueOf(Math.max(endpointConfiguration.getMaxMessageSize(), req.getMaxResponseMessageSize().longValue())));
+            res.setRevisedSessionTimeout(Math.max(req.getRequestedSessionTimeout(), 60 * 1000));
+            KeyPair cert = getApplication().getApplicationInstanceCertificates()[0];
+            res.setServerCertificate(ByteString.valueOf(cert.getCertificate().getEncoded()));
+            res.setServerEndpoints(this.getEndpointDescriptions());
+            res.setServerNonce(ByteString.valueOf(nonce));
+            ByteString clientCertificate = req.getClientCertificate();
+            ByteString clientNonce = req.getClientNonce();
+            SecurityPolicy securityPolicy = msgExchange.getChannel().getSecurityPolicy();
+            res.setServerSignature(
+                    getServerSignature(clientCertificate, clientNonce, securityPolicy, cert.getPrivateKey().getPrivateKey()));
+
+            res.setServerSoftwareCertificates(getApplication().getSoftwareCertificates());
+            res.setSessionId(new NodeId(0, "Session-" + UUID.randomUUID()));
+            msgExchange.sendResponse(res);
+        }
+
+        private SignatureData getServerSignature(ByteString clientCertificate, ByteString clientNonce,
+                                                 SecurityPolicy securityPolicy, final RSAPrivateKey privateKey) throws ServiceFaultException {
+            if (clientCertificate != null) {
+                ByteArrayOutputStream s = new ByteArrayOutputStream();
+                try {
+                    s.write(clientCertificate.getValue());
+                } catch (IOException e) {
+                    throw new ServiceFaultException(ServiceFault.createServiceFault(StatusCodes.Bad_SecurityChecksFailed));
+                } catch (Exception e) {
+                    throw new ServiceFaultException(ServiceFault.createServiceFault(StatusCodes.Bad_NonceInvalid));
+                }
+                try {
+                    s.write(clientNonce.getValue());
+                } catch (IOException e) {
+                    throw new ServiceFaultException(ServiceFault.createServiceFault(StatusCodes.Bad_NonceInvalid));
+                } catch (Exception e) {
+                    throw new ServiceFaultException(ServiceFault.createServiceFault(StatusCodes.Bad_NonceInvalid));
+                }
+                try {
+                    SecurityAlgorithm algorithm = securityPolicy.getAsymmetricSignatureAlgorithm();
+                    return new SignatureData(algorithm.getUri(),
+                            ByteString.valueOf(CryptoUtil.getCryptoProvider().signAsymm(privateKey, algorithm, s.toByteArray())));
+
+                } catch (ServiceResultException e) {
+                    throw new ServiceFaultException(e);
+                }
+            }
+            return null;
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        ////////////// SERVER //////////////
+        // Create UA Server Application
+        // Create UA Service Server
+        Application myServerApplication = new Application();
+        MyServerExample myServer = new MyServerExample(myServerApplication);
+
+        myServer.addServiceHandler(new MyNodeManagementServiceHandler());
+        myServer.addServiceHandler(new MyAttributeServiceHandler());
+
+        //////////////////////////////////////
+        // Press enter to shutdown
+        System.out.println("Press enter to shutdown");
+        System.in.read();
+        //////////////////////////////////////
+
+
+        ///////////// SHUTDOWN /////////////
+        // Close the server by unbinding all endpoints
+        myServer.getApplication().close();
+        //////////////////////////////////////
+
+    }
+
+}

+ 28 - 0
src/main/resources/META-INF/LICENSE

@@ -0,0 +1,28 @@
+Example:
+Copyright (c) <year>, <copyright holder>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+   must display the following acknowledgement:
+   This product includes software developed by the <organization>.
+4. Neither the name of the <organization> nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 28 - 0
src/main/resources/at/acdp/opcur/installation.html

@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<title>Hello World</title>
+		<style>
+			input {
+				display: inline-block;
+				width: 200px;
+				height: 28px;
+			}
+
+			label {
+				display: inline-block;
+				width: 100px;
+				height: 28px;
+			}
+		</style>
+	</head>
+	<body>
+		<h1>Hello World</h1>
+		<form>
+			<p>The popup title below is shared between all Hello World program nodes.</p><br \>
+			<p>The title cannot be empty.</p><br \>
+			<div class="spacer">&nbsp;</div>
+			<label>Popup title:</label><input id="popuptitle" type="text"/>
+		</form>
+	</body>
+</html>

+ 40 - 0
src/main/resources/at/acdp/opcur/programnode.html

@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<title>Hello World</title>
+		<style>
+			input {
+				display: inline-block;
+				width: 200px;
+				height: 28px;
+			}
+
+			label {
+				display: inline-block;
+				width: 150px;
+				height: 28px;
+			}
+
+			#preview {
+				display: block;
+				padding: 35px 20px 20px 0px;
+			}
+
+			#header {
+				padding: 0px 0px 20px 0px;
+			}
+		</style>
+	</head>
+	<body>
+		<form>
+			<p>This program node will open a popup on execution.</p><br \>
+			<label>Enter your name:</label> <input id="yourname" type="text"/>
+			<br/>
+			<div id="preview">
+				<h3 id="header">Preview</h3><br \>
+				Title: <label id="titlePreviewLabel" style="width: 400px;"/> <br \>
+				Message: <label id="messagePreviewLabel" style="width: 400px;"/>
+			</div>
+		</form>
+	</body>
+</html>