1 /* 2 * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include "JavaInputStreamCallbacks.h" 27 #include "JniUtils.h" 28 #include <Common/VSMemory.h> 29 #if TARGET_OS_LINUX 30 #include <stdlib.h> 31 #include <string.h> 32 #endif // TARGET_OS_LINUX 33 34 jfieldID CJavaInputStreamCallbacks::m_BufferFID = 0; 35 jmethodID CJavaInputStreamCallbacks::m_NeedBufferMID = 0; 36 jmethodID CJavaInputStreamCallbacks::m_ReadNextBlockMID = 0; 37 jmethodID CJavaInputStreamCallbacks::m_ReadBlockMID = 0; 38 jmethodID CJavaInputStreamCallbacks::m_IsSeekableMID = 0; 39 jmethodID CJavaInputStreamCallbacks::m_IsRandomAccessMID = 0; 40 jmethodID CJavaInputStreamCallbacks::m_SeekMID = 0; 41 jmethodID CJavaInputStreamCallbacks::m_CloseConnectionMID = 0; 42 jmethodID CJavaInputStreamCallbacks::m_PropertyMID = 0; 43 jmethodID CJavaInputStreamCallbacks::m_GetStreamSizeMID = 0; 44 45 CJavaInputStreamCallbacks::CJavaInputStreamCallbacks() 46 : m_ConnectionHolder(0) 47 {} 48 49 CJavaInputStreamCallbacks::~CJavaInputStreamCallbacks() 50 {} 51 52 bool CJavaInputStreamCallbacks::Init(JNIEnv *env, jobject jLocator) 53 { 54 env->GetJavaVM(&m_jvm); 55 CJavaEnvironment javaEnv(m_jvm); 56 57 static jmethodID createConnectionHolder = 0; 58 if (0 == createConnectionHolder) 59 { 60 jclass klass = env->GetObjectClass(jLocator); 61 createConnectionHolder = env->GetMethodID(klass, "createConnectionHolder", "()Lcom/sun/media/jfxmedia/locator/ConnectionHolder;"); 62 env->DeleteLocalRef(klass); 63 } 64 65 m_ConnectionHolder = env->NewGlobalRef(env->CallObjectMethod(jLocator, createConnectionHolder)); 66 if (NULL == m_ConnectionHolder) 67 { 68 javaEnv.reportException(); 69 return false; 70 } 71 72 static bool methodIDsInitialized = false; 73 if (!methodIDsInitialized) 74 { 75 // Get the parent abstract class. It's wrong to get method ids from the concrete implementation 76 // because it crashes jvm when it tries to call virtual methods. 77 // See https://javafx-jira.kenai.com/browse/RT-37115 78 jclass klass = env->FindClass("com/sun/media/jfxmedia/locator/ConnectionHolder"); 79 80 m_BufferFID = env->GetFieldID(klass, "buffer", "Ljava/nio/ByteBuffer;"); 81 m_NeedBufferMID = env->GetMethodID(klass, "needBuffer", "()Z"); 82 m_ReadNextBlockMID = env->GetMethodID(klass, "readNextBlock", "()I"); 83 m_ReadBlockMID = env->GetMethodID(klass, "readBlock", "(JI)I"); 84 m_IsSeekableMID = env->GetMethodID(klass, "isSeekable", "()Z"); 85 m_IsRandomAccessMID = env->GetMethodID(klass, "isRandomAccess", "()Z"); 86 m_SeekMID = env->GetMethodID(klass, "seek", "(J)J"); 87 m_CloseConnectionMID = env->GetMethodID(klass, "closeConnection", "()V"); 88 m_PropertyMID = env->GetMethodID(klass, "property", "(II)I"); 89 m_GetStreamSizeMID = env->GetMethodID(klass, "getStreamSize", "()I"); 90 91 methodIDsInitialized = true; 92 env->DeleteLocalRef(klass); 93 } 94 95 javaEnv.reportException(); 96 return true; 97 } 98 99 bool CJavaInputStreamCallbacks::NeedBuffer() 100 { 101 bool result = false; 102 CJavaEnvironment javaEnv(m_jvm); 103 JNIEnv *pEnv = javaEnv.getEnvironment(); 104 105 if (pEnv) { 106 jobject connection = pEnv->NewLocalRef(m_ConnectionHolder); 107 if (connection) { 108 result = (pEnv->CallBooleanMethod(connection, m_NeedBufferMID) == JNI_TRUE); 109 pEnv->DeleteLocalRef(connection); 110 } 111 112 javaEnv.reportException(); 113 } 114 115 return result; 116 } 117 118 int CJavaInputStreamCallbacks::ReadNextBlock() 119 { 120 int result = -1; 121 CJavaEnvironment javaEnv(m_jvm); 122 JNIEnv *pEnv = javaEnv.getEnvironment(); 123 124 if (pEnv) { 125 jobject connection = pEnv->NewLocalRef(m_ConnectionHolder); 126 if (connection) { 127 result = pEnv->CallIntMethod(connection, m_ReadNextBlockMID); 128 pEnv->DeleteLocalRef(connection); 129 } 130 131 if (javaEnv.clearException()) { 132 result = -2; 133 } 134 } 135 136 return result; 137 } 138 139 int CJavaInputStreamCallbacks::ReadBlock(int64_t position, int size) 140 { 141 int result = -1; 142 CJavaEnvironment javaEnv(m_jvm); 143 JNIEnv *pEnv = javaEnv.getEnvironment(); 144 145 if (pEnv) { 146 jobject connection = pEnv->NewLocalRef(m_ConnectionHolder); 147 if (connection) { 148 result = pEnv->CallIntMethod(connection, m_ReadBlockMID, position, size); 149 pEnv->DeleteLocalRef(connection); 150 } 151 152 if (javaEnv.clearException()) { 153 result = -2; 154 } 155 } 156 157 return result; 158 } 159 160 void CJavaInputStreamCallbacks::CopyBlock(void* destination, int size) 161 { 162 CJavaEnvironment javaEnv(m_jvm); 163 JNIEnv *pEnv = javaEnv.getEnvironment(); 164 if (pEnv) { 165 jobject connection = pEnv->NewLocalRef(m_ConnectionHolder); 166 if (connection) { 167 jobject buffer = pEnv->GetObjectField(connection, m_BufferFID); 168 void *data = pEnv->GetDirectBufferAddress(buffer); 169 170 memcpy(destination, data, size); 171 pEnv->DeleteLocalRef(buffer); 172 pEnv->DeleteLocalRef(connection); 173 } 174 } 175 } 176 177 bool CJavaInputStreamCallbacks::IsSeekable() 178 { 179 CJavaEnvironment javaEnv(m_jvm); 180 JNIEnv *pEnv = javaEnv.getEnvironment(); 181 bool result = false; 182 183 if (pEnv) { 184 jobject connection = pEnv->NewLocalRef(m_ConnectionHolder); 185 if (connection) { 186 result = (pEnv->CallBooleanMethod(connection, m_IsSeekableMID) == JNI_TRUE); 187 pEnv->DeleteLocalRef(connection); 188 } 189 190 javaEnv.reportException(); 191 } 192 193 return result; 194 } 195 196 bool CJavaInputStreamCallbacks::IsRandomAccess() 197 { 198 CJavaEnvironment javaEnv(m_jvm); 199 JNIEnv *pEnv = javaEnv.getEnvironment(); 200 bool result = false; 201 202 if (pEnv) { 203 jobject connection = pEnv->NewLocalRef(m_ConnectionHolder); 204 if (connection) { 205 result = (pEnv->CallBooleanMethod(connection, m_IsRandomAccessMID) == JNI_TRUE); 206 pEnv->DeleteLocalRef(connection); 207 } 208 209 javaEnv.reportException(); 210 } 211 212 return result; 213 } 214 215 int64_t CJavaInputStreamCallbacks::Seek(int64_t position) 216 { 217 CJavaEnvironment javaEnv(m_jvm); 218 JNIEnv *pEnv = javaEnv.getEnvironment(); 219 jlong result = -1; 220 221 if (pEnv) { 222 jobject connection = pEnv->NewLocalRef(m_ConnectionHolder); 223 if (connection) { 224 result = pEnv->CallLongMethod(connection, m_SeekMID, (jlong)position); 225 pEnv->DeleteLocalRef(connection); 226 } 227 228 javaEnv.reportException(); 229 } 230 231 return (int64_t)result; 232 } 233 234 void CJavaInputStreamCallbacks::CloseConnection() 235 { 236 CJavaEnvironment javaEnv(m_jvm); 237 JNIEnv *pEnv = javaEnv.getEnvironment(); 238 239 if (pEnv) { 240 jobject connection = pEnv->NewLocalRef(m_ConnectionHolder); 241 if (connection) { 242 pEnv->CallVoidMethod(connection, m_CloseConnectionMID); 243 pEnv->DeleteLocalRef(connection); 244 245 javaEnv.reportException(); 246 } 247 248 pEnv->DeleteGlobalRef(m_ConnectionHolder); 249 m_ConnectionHolder = NULL; 250 } 251 } 252 253 int CJavaInputStreamCallbacks::Property(int prop, int value) 254 { 255 CJavaEnvironment javaEnv(m_jvm); 256 JNIEnv *pEnv = javaEnv.getEnvironment(); 257 int result = 0; 258 259 if (pEnv) { 260 jobject connection = pEnv->NewLocalRef(m_ConnectionHolder); 261 if (connection) { 262 result = pEnv->CallIntMethod(connection, m_PropertyMID, (jint)prop, (jint)value); 263 pEnv->DeleteLocalRef(connection); 264 } 265 266 javaEnv.reportException(); 267 } 268 269 return result; 270 } 271 272 int CJavaInputStreamCallbacks::GetStreamSize() 273 { 274 CJavaEnvironment javaEnv(m_jvm); 275 JNIEnv *pEnv = javaEnv.getEnvironment(); 276 int result = 0; 277 278 if (pEnv) { 279 jobject connection = pEnv->NewLocalRef(m_ConnectionHolder); 280 if (connection) { 281 result = pEnv->CallIntMethod(connection, m_GetStreamSizeMID); 282 pEnv->DeleteLocalRef(connection); 283 } 284 285 javaEnv.reportException(); 286 } 287 288 return result; 289 }