]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.procore.server.environment/src/org/simantics/db/procore/server/environment/windows/Msi.java
19a55c494bcd29711a538c8c0d763d135462974d
[simantics/platform.git] / bundles / org.simantics.db.procore.server.environment / src / org / simantics / db / procore / server / environment / windows / Msi.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.db.procore.server.environment.windows;
13
14 import java.io.Closeable;
15 import java.io.File;
16 import java.io.FileInputStream;
17 import java.io.FileNotFoundException;
18 import java.io.FileOutputStream;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.net.URL;
22 import java.net.URLDecoder;
23 import java.security.MessageDigest;
24 import java.security.NoSuchAlgorithmException;
25 import java.util.Arrays;
26
27 import org.simantics.db.procore.server.environment.ExecutionEnvironment;
28 import org.simantics.db.procore.server.environment.ExecutionEnvironmentException;
29
30 /**
31  * Provides Java access to Windows Msi API functions:
32  * <ul>
33  * <li>{@link #MsiQueryProductState(String)} (see <a
34  * href="http://msdn.microsoft.com/en-us/library/aa370363(VS.85).aspx">MSDN</a>)</li>
35  * </ul>
36  * 
37  * @author Tuukka Lehtonen
38  */
39 public class Msi {
40
41     public static void checkOneOfProductsInstalled(Product... products) throws ExecutionEnvironmentException {
42         StringBuilder sb = new StringBuilder();
43         sb.append("None of the following products are installed properly:\n");
44         for (Product product : products) {
45             ProductState state = Msi.MsiQueryProductState(product.getCode());
46             switch (state) {
47                 case DEFAULT:
48                     // One of the listed products is installed OK.
49                     return;
50                 default:
51                     sb.append("\t" + product);
52                     sb.append(" - MsiQueryProductState returned ");
53                     sb.append(state);
54                     sb.append("\n");
55             }
56         }
57         throw new ExecutionEnvironmentException("Cannot run ProCore database server in this environment due to the following problems:\n" + sb.toString(), Arrays.asList(products));
58     }
59
60     public static void checkProductsInstalled(Product... products) throws ExecutionEnvironmentException {
61         StringBuilder sb = new StringBuilder();
62         for (Product product : products) {
63             ProductState state = Msi.MsiQueryProductState(product.getCode());
64             switch (state) {
65                 case DEFAULT:
66                     // Installed OK
67                     continue;
68                 default:
69                     sb.append("\tProduct " + product + " is not installed properly, MsiQueryProductState returned " + state);
70                     sb.append("\n");
71             }
72         }
73         if (sb.length() > 0) {
74             // Something is not installed right, throw exception
75             throw new ExecutionEnvironmentException("Cannot run ProCore database server in this environment due to the following problems:\n" + sb.toString(), Arrays.asList(products));
76         }
77     }
78
79     // -------------------------------------------------------------------------
80
81     public static ProductState MsiQueryProductState(String productCode) {
82         int result = MsiQueryProductState0(productCode);
83         return ProductState.of(result);
84     }
85
86     private static native int MsiQueryProductState0(String productCode);
87
88     // -------------------------------------------------------------------------
89
90     private static final String DLL_NAME = "msijni.dll";
91     private static final String DLL_NAME_64 = "msijni64.dll";
92
93     static {
94         initialize();
95     }
96
97     private static void initialize() {
98         ExecutionEnvironment env = ExecutionEnvironment.calculate();
99         String libName = null;
100         switch (env.arch) {
101             case X86:
102                 libName = DLL_NAME;
103                 break;
104             case X86_64:
105                 libName = DLL_NAME_64;
106                 break;
107             default:
108                 return;
109         }
110
111         URL libURL = Msi.class.getResource("/" + libName);
112         if (libURL != null) {
113             try {
114                 if ("file".equals(libURL.getProtocol())) {
115                     File path = new File(URLDecoder.decode(libURL.getPath(), "UTF-8"));
116                     initialize(path);
117                 } else {
118                     File libFile = extractLib(libURL, libName);
119                     initialize(libFile);
120                 }
121             } catch (IOException e) {
122                 e.printStackTrace();
123             }
124         }
125     }
126
127     private static void initialize(File path) {
128         //System.out.println("load msijni: " + path);
129         System.load(path.toString());
130     }
131
132     /**
133      * Extracts the specified source file in the specified bundle into the
134      * specified local directory.
135      * 
136      * @param url the source URL to stream the resource from
137      * @param targetFile the target file to write the resource to
138      * @param deleteOnExit <code>true</code> to use {@link File#deleteOnExit()}
139      *        on the resulting file. Note that this does not guarantee that the
140      *        file is deleted when the JVM exits
141      * @return the resulting file
142      * @throws FileNotFoundException
143      */
144     private static File copyResource(URL url, File targetFile) throws IOException, FileNotFoundException {
145         FileOutputStream os = null;
146         InputStream is = null;
147         try {
148             if (targetFile.exists())
149                 targetFile.delete();
150
151             is = url.openStream();
152             int read;
153             byte [] buffer = new byte [16384];
154             os = new FileOutputStream (targetFile);
155             while ((read = is.read (buffer)) != -1) {
156                 os.write(buffer, 0, read);
157             }
158             os.close ();
159             is.close ();
160
161             return targetFile;
162         } finally {
163             uncheckedClose(os);
164             uncheckedClose(is);
165         }
166     }
167
168
169     private static void uncheckedClose(Closeable closeable) {
170         try {
171             if (closeable != null)
172                 closeable.close();
173         } catch (IOException e) {
174             //ignore
175         }
176     }
177
178     private static File extractLib(URL libURL, String libName) throws FileNotFoundException, IOException {
179         String tmpDirStr = System.getProperty("java.io.tmpdir");
180         if (tmpDirStr == null)
181             throw new NullPointerException("java.io.tmpdir property is null");
182         File tmpDir = new File(tmpDirStr);
183         File libFile = new File(tmpDir, libName);
184
185         if (libFile.exists()) {
186             if (libFile.isFile()) {
187                 try {
188                     byte[] origSum = computeSum(libURL);
189                     byte[] targetSum = computeSum(libFile);
190                     if (Arrays.equals(origSum, targetSum))
191                         return libFile;
192                 } catch (NoSuchAlgorithmException e) {
193                     // TODO Auto-generated catch block
194                     e.printStackTrace();
195                 }
196             }
197         }
198
199         return copyResource(libURL, libFile);
200     }
201
202     public static byte[] computeSum(InputStream in) throws IOException {
203         if (in == null)
204             throw new IllegalArgumentException("Input cannot be null!");
205
206         try {
207             MessageDigest md = MessageDigest.getInstance("MD5");
208             byte[] data = new byte[64 * 1024];
209
210             while (true) {
211                 int read = in.read(data);
212                 if (read == -1) {
213                     return md.digest();
214                 }
215                 md.update(data, 0, read);
216             }
217         } catch (NoSuchAlgorithmException e) {
218             // Should not be possible for MD5
219             throw new IOException(e);
220         }
221     }
222
223     public static byte[] computeSum(File f) throws IOException, NoSuchAlgorithmException {
224         InputStream in = null;
225         try {
226             in = new FileInputStream(f);
227             return computeSum(in);
228         } finally {
229             if (in != null)
230                 in.close();
231         }
232     }
233
234     public static byte[] computeSum(URL url) throws IOException, NoSuchAlgorithmException {
235         InputStream in = null;
236         try {
237             in = url.openStream();
238             return computeSum(in);
239         } finally {
240             if (in != null)
241                 in.close();
242         }
243     }
244
245 }