1/*
2 * Copyright (C) 2010 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.clearsilver.jni;
18
19import java.io.File;
20import java.util.regex.Pattern;
21
22/**
23 * Loads the ClearSilver JNI library.
24 *
25 * <p>By default, it attempts to load the library 'clearsilver-jni' from the
26 * path specified in the 'java.library.path' system property. However, this
27 * can be overriden by calling {@link #setLibraryName(String)} and
28 * {@link #setLibrarySearchPaths(String[])}.</p>
29 *
30 * <p>If this fails, the JVM exits with a code of 1. However, this strategy
31 * can be changed using {@link #setFailureCallback(Runnable)}.</p>
32 */
33public final class JNI {
34
35  /**
36   * Failure callback strategy that writes a message to sysout, then calls
37   * System.exit(1).
38   */
39  public static Runnable EXIT_JVM = new Runnable() {
40    public void run() {
41      System.err.println("Could not load '" + libraryName + "'. Searched:");
42      String platformLibraryName = System.mapLibraryName(libraryName);
43      for (String path : librarySearchPaths) {
44        System.err.println("  " +
45            new File(path, platformLibraryName).getAbsolutePath());
46      }
47      System.err.println(
48          "Try specifying -Djava.library.path=[directory] or calling "
49              + JNI.class.getName() + ".setLibrarySearchPaths(String...)");
50      System.exit(1);
51    }
52  };
53
54  /**
55   * Failure callback strategy that throws an UnsatisfiedLinkError, which
56   * should be caught be client code.
57   */
58  public static Runnable THROW_ERROR = new Runnable() {
59    public void run() {
60      throw new UnsatisfiedLinkError("Could not load '" + libraryName + "'");
61    }
62  };
63
64  private static Runnable failureCallback = EXIT_JVM;
65
66  private static Object callbackLock = new Object();
67
68  private static String libraryName = "clearsilver-jni";
69
70  private static String[] librarySearchPaths
71      = System.getProperty("java.library.path", ".").split(
72          Pattern.quote(File.pathSeparator));
73
74  private static volatile boolean successfullyLoadedLibrary;
75
76  /**
77   * Attempts to load the ClearSilver JNI library.
78   *
79   * @see #setFailureCallback(Runnable)
80   */
81  public static void loadLibrary() {
82
83    // Library already loaded? Great - nothing to do.
84    if (successfullyLoadedLibrary) {
85      return;
86    }
87
88    synchronized (callbackLock) {
89
90      // Search librarySearchPaths...
91      String platformLibraryName = System.mapLibraryName(libraryName);
92      for (String path : librarySearchPaths) {
93        try {
94          // Attempt to load the library in that path.
95          System.load(new File(path, platformLibraryName).getAbsolutePath());
96          // If we got here, it worked. We're done.
97          successfullyLoadedLibrary = true;
98          return;
99        } catch (UnsatisfiedLinkError e) {
100          // Library not found. Continue loop.
101        }
102      }
103
104      // Still here? Couldn't load library. Fail.
105      if (failureCallback != null) {
106        failureCallback.run();
107      }
108    }
109
110  }
111
112  /**
113   * Sets a callback for what should happen if the JNI library cannot
114   * be loaded. The default is {@link #EXIT_JVM}.
115   *
116   * @see #EXIT_JVM
117   * @see #THROW_ERROR
118   */
119  public static void setFailureCallback(Runnable failureCallback) {
120    synchronized(callbackLock) {
121      JNI.failureCallback = failureCallback;
122    }
123  }
124
125  /**
126   * Set name of JNI library to load. Default is 'clearsilver-jni'.
127   */
128  public static void setLibraryName(String libraryName) {
129    JNI.libraryName = libraryName;
130  }
131
132  /**
133   * Sets locations where JNI library is searched.
134   */
135  public static void setLibrarySearchPaths(String... paths) {
136    JNI.librarySearchPaths = paths;
137  }
138
139}
140