1 /*
2 * Copyright 2016 The Netty Project
3 *
4 * The Netty Project licenses this file to you under the Apache License,
5 * version 2.0 (the "License"); you may not use this file except in compliance
6 * with the License. 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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16 /*
17 * Licensed to the Apache Software Foundation (ASF) under one or more
18 * contributor license agreements. See the NOTICE file distributed with
19 * this work for additional information regarding copyright ownership.
20 * The ASF licenses this file to You under the Apache License, Version 2.0
21 * (the "License"); you may not use this file except in compliance with
22 * the License. You may obtain a copy of the License at
23 *
24 * http://www.apache.org/licenses/LICENSE-2.0
25 *
26 * Unless required by applicable law or agreed to in writing, software
27 * distributed under the License is distributed on an "AS IS" BASIS,
28 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29 * See the License for the specific language governing permissions and
30 * limitations under the License.
31 */
32
33 package io.netty.internal.tcnative;
34
35 import java.io.File;
36
37 public final class Library {
38
39 /* Default library names */
40 private static final String [] NAMES = {
41 "netty_tcnative",
42 "libnetty_tcnative"
43 };
44
45 private static final String PROVIDED = "provided";
46
47 /*
48 * A handle to the unique Library singleton instance.
49 */
50 private static Library _instance = null;
51
52 private Library() throws Exception {
53 boolean loaded = false;
54 String path = System.getProperty("java.library.path");
55 String [] paths = path.split(File.pathSeparator);
56 StringBuilder err = new StringBuilder();
57 for (int i = 0; i < NAMES.length; i++) {
58 try {
59 loadLibrary(NAMES[i]);
60 loaded = true;
61 } catch (ThreadDeath t) {
62 throw t;
63 } catch (VirtualMachineError t) {
64 throw t;
65 } catch (Throwable t) {
66 String name = System.mapLibraryName(NAMES[i]);
67 for (int j = 0; j < paths.length; j++) {
68 File fd = new File(paths[j] , name);
69 if (fd.exists()) {
70 // File exists but failed to load
71 throw new RuntimeException(t);
72 }
73 }
74 if (i > 0) {
75 err.append(", ");
76 }
77 err.append(t.getMessage());
78 }
79 if (loaded) {
80 break;
81 }
82 }
83 if (!loaded) {
84 throw new UnsatisfiedLinkError(err.toString());
85 }
86 }
87
88 private Library(String libraryName) {
89 if (!PROVIDED.equals(libraryName)) {
90 loadLibrary(libraryName);
91 }
92 }
93
94 private static void loadLibrary(String libraryName) {
95 System.loadLibrary(calculatePackagePrefix().replace('.', '_') + libraryName);
96 }
97
98 /**
99 * The shading prefix added to this class's full name.
100 *
101 * @throws UnsatisfiedLinkError if the shader used something other than a prefix
102 */
103 private static String calculatePackagePrefix() {
104 String maybeShaded = Library.class.getName();
105 // Use ! instead of . to avoid shading utilities from modifying the string
106 String expected = "io!netty!internal!tcnative!Library".replace('!', '.');
107 if (!maybeShaded.endsWith(expected)) {
108 throw new UnsatisfiedLinkError(String.format(
109 "Could not find prefix added to %s to get %s. When shading, only adding a "
110 + "package prefix is supported", expected, maybeShaded));
111 }
112 return maybeShaded.substring(0, maybeShaded.length() - expected.length());
113 }
114
115 /* create global TCN's APR pool
116 * This has to be the first call to TCN library.
117 */
118 private static native boolean initialize0();
119
120 private static native boolean aprHasThreads();
121
122 private static native int aprMajorVersion();
123
124 /* APR_VERSION_STRING */
125 private static native String aprVersionString();
126
127 /**
128 * Calls {@link #initialize(String, String)} with {@code "provided"} and {@code null}.
129 *
130 * @return {@code true} if initialization was successful
131 * @throws Exception if an error happens during initialization
132 */
133 public static boolean initialize() throws Exception {
134 return initialize(PROVIDED, null);
135 }
136
137 /**
138 * Setup native library. This is the first method that must be called!
139 *
140 * @param libraryName the name of the library to load
141 * @param engine Support for external a Crypto Device ("engine"), usually
142 * @return {@code true} if initialization was successful
143 * @throws Exception if an error happens during initialization
144 */
145 public static boolean initialize(String libraryName, String engine) throws Exception {
146 if (_instance == null) {
147 _instance = libraryName == null ? new Library() : new Library(libraryName);
148
149 if (aprMajorVersion() < 1) {
150 throw new UnsatisfiedLinkError("Unsupported APR Version (" +
151 aprVersionString() + ")");
152 }
153
154 if (!aprHasThreads()) {
155 throw new UnsatisfiedLinkError("Missing APR_HAS_THREADS");
156 }
157 }
158 return initialize0() && SSL.initialize(engine) == 0;
159 }
160 }