+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.db.procore.server.environment.windows;\r
+\r
+import java.io.Closeable;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.net.URL;\r
+import java.net.URLDecoder;\r
+import java.security.MessageDigest;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.util.Arrays;\r
+\r
+import org.simantics.db.procore.server.environment.ExecutionEnvironment;\r
+import org.simantics.db.procore.server.environment.ExecutionEnvironmentException;\r
+\r
+/**\r
+ * Provides Java access to Windows Msi API functions:\r
+ * <ul>\r
+ * <li>{@link #MsiQueryProductState(String)} (see <a\r
+ * href="http://msdn.microsoft.com/en-us/library/aa370363(VS.85).aspx">MSDN</a>)</li>\r
+ * </ul>\r
+ * \r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class Msi {\r
+\r
+ public static void checkOneOfProductsInstalled(Product... products) throws ExecutionEnvironmentException {\r
+ StringBuilder sb = new StringBuilder();\r
+ sb.append("None of the following products are installed properly:\n");\r
+ for (Product product : products) {\r
+ ProductState state = Msi.MsiQueryProductState(product.getCode());\r
+ switch (state) {\r
+ case DEFAULT:\r
+ // One of the listed products is installed OK.\r
+ return;\r
+ default:\r
+ sb.append("\t" + product);\r
+ sb.append(" - MsiQueryProductState returned ");\r
+ sb.append(state);\r
+ sb.append("\n");\r
+ }\r
+ }\r
+ throw new ExecutionEnvironmentException("Cannot run ProCore database server in this environment due to the following problems:\n" + sb.toString(), Arrays.asList(products));\r
+ }\r
+\r
+ public static void checkProductsInstalled(Product... products) throws ExecutionEnvironmentException {\r
+ StringBuilder sb = new StringBuilder();\r
+ for (Product product : products) {\r
+ ProductState state = Msi.MsiQueryProductState(product.getCode());\r
+ switch (state) {\r
+ case DEFAULT:\r
+ // Installed OK\r
+ continue;\r
+ default:\r
+ sb.append("\tProduct " + product + " is not installed properly, MsiQueryProductState returned " + state);\r
+ sb.append("\n");\r
+ }\r
+ }\r
+ if (sb.length() > 0) {\r
+ // Something is not installed right, throw exception\r
+ throw new ExecutionEnvironmentException("Cannot run ProCore database server in this environment due to the following problems:\n" + sb.toString(), Arrays.asList(products));\r
+ }\r
+ }\r
+\r
+ // -------------------------------------------------------------------------\r
+\r
+ public static ProductState MsiQueryProductState(String productCode) {\r
+ int result = MsiQueryProductState0(productCode);\r
+ return ProductState.of(result);\r
+ }\r
+\r
+ private static native int MsiQueryProductState0(String productCode);\r
+\r
+ // -------------------------------------------------------------------------\r
+\r
+ private static final String DLL_NAME = "msijni.dll";\r
+ private static final String DLL_NAME_64 = "msijni64.dll";\r
+\r
+ static {\r
+ initialize();\r
+ }\r
+\r
+ private static void initialize() {\r
+ ExecutionEnvironment env = ExecutionEnvironment.calculate();\r
+ String libName = null;\r
+ switch (env.arch) {\r
+ case X86:\r
+ libName = DLL_NAME;\r
+ break;\r
+ case X86_64:\r
+ libName = DLL_NAME_64;\r
+ break;\r
+ default:\r
+ return;\r
+ }\r
+\r
+ URL libURL = Msi.class.getResource("/" + libName);\r
+ if (libURL != null) {\r
+ try {\r
+ if ("file".equals(libURL.getProtocol())) {\r
+ File path = new File(URLDecoder.decode(libURL.getPath(), "UTF-8"));\r
+ initialize(path);\r
+ } else {\r
+ File libFile = extractLib(libURL, libName);\r
+ initialize(libFile);\r
+ }\r
+ } catch (IOException e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ }\r
+\r
+ private static void initialize(File path) {\r
+ //System.out.println("load msijni: " + path);\r
+ System.load(path.toString());\r
+ }\r
+\r
+ /**\r
+ * Extracts the specified source file in the specified bundle into the\r
+ * specified local directory.\r
+ * \r
+ * @param url the source URL to stream the resource from\r
+ * @param targetFile the target file to write the resource to\r
+ * @param deleteOnExit <code>true</code> to use {@link File#deleteOnExit()}\r
+ * on the resulting file. Note that this does not guarantee that the\r
+ * file is deleted when the JVM exits\r
+ * @return the resulting file\r
+ * @throws FileNotFoundException\r
+ */\r
+ private static File copyResource(URL url, File targetFile) throws IOException, FileNotFoundException {\r
+ FileOutputStream os = null;\r
+ InputStream is = null;\r
+ try {\r
+ if (targetFile.exists())\r
+ targetFile.delete();\r
+\r
+ is = url.openStream();\r
+ int read;\r
+ byte [] buffer = new byte [16384];\r
+ os = new FileOutputStream (targetFile);\r
+ while ((read = is.read (buffer)) != -1) {\r
+ os.write(buffer, 0, read);\r
+ }\r
+ os.close ();\r
+ is.close ();\r
+\r
+ return targetFile;\r
+ } finally {\r
+ uncheckedClose(os);\r
+ uncheckedClose(is);\r
+ }\r
+ }\r
+\r
+\r
+ private static void uncheckedClose(Closeable closeable) {\r
+ try {\r
+ if (closeable != null)\r
+ closeable.close();\r
+ } catch (IOException e) {\r
+ //ignore\r
+ }\r
+ }\r
+\r
+ private static File extractLib(URL libURL, String libName) throws FileNotFoundException, IOException {\r
+ String tmpDirStr = System.getProperty("java.io.tmpdir");\r
+ if (tmpDirStr == null)\r
+ throw new NullPointerException("java.io.tmpdir property is null");\r
+ File tmpDir = new File(tmpDirStr);\r
+ File libFile = new File(tmpDir, libName);\r
+\r
+ if (libFile.exists()) {\r
+ if (libFile.isFile()) {\r
+ try {\r
+ byte[] origSum = computeSum(libURL);\r
+ byte[] targetSum = computeSum(libFile);\r
+ if (Arrays.equals(origSum, targetSum))\r
+ return libFile;\r
+ } catch (NoSuchAlgorithmException e) {\r
+ // TODO Auto-generated catch block\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ }\r
+\r
+ return copyResource(libURL, libFile);\r
+ }\r
+\r
+ public static byte[] computeSum(InputStream in) throws IOException {\r
+ if (in == null)\r
+ throw new IllegalArgumentException("Input cannot be null!");\r
+\r
+ try {\r
+ MessageDigest md = MessageDigest.getInstance("MD5");\r
+ byte[] data = new byte[64 * 1024];\r
+\r
+ while (true) {\r
+ int read = in.read(data);\r
+ if (read == -1) {\r
+ return md.digest();\r
+ }\r
+ md.update(data, 0, read);\r
+ }\r
+ } catch (NoSuchAlgorithmException e) {\r
+ // Should not be possible for MD5\r
+ throw new IOException(e);\r
+ }\r
+ }\r
+\r
+ public static byte[] computeSum(File f) throws IOException, NoSuchAlgorithmException {\r
+ InputStream in = null;\r
+ try {\r
+ in = new FileInputStream(f);\r
+ return computeSum(in);\r
+ } finally {\r
+ if (in != null)\r
+ in.close();\r
+ }\r
+ }\r
+\r
+ public static byte[] computeSum(URL url) throws IOException, NoSuchAlgorithmException {\r
+ InputStream in = null;\r
+ try {\r
+ in = url.openStream();\r
+ return computeSum(in);\r
+ } finally {\r
+ if (in != null)\r
+ in.close();\r
+ }\r
+ }\r
+\r
+}
\ No newline at end of file