1 /*
   2  * Copyright (c) 2010, 2014, 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     if (m_ConnectionHolder && pEnv)
 105     {
 106         result = (pEnv->CallBooleanMethod(m_ConnectionHolder, m_NeedBufferMID) == JNI_TRUE);
 107         javaEnv.reportException();
 108     }
 109 
 110     return result;
 111 }
 112 
 113 int CJavaInputStreamCallbacks::ReadNextBlock()
 114 {
 115     int result = -1;
 116     CJavaEnvironment javaEnv(m_jvm);
 117     JNIEnv *pEnv = javaEnv.getEnvironment();
 118 
 119     if (m_ConnectionHolder && pEnv) {
 120         result = pEnv->CallIntMethod(m_ConnectionHolder, m_ReadNextBlockMID);
 121         if (javaEnv.clearException()) {
 122             result = -2;
 123         }
 124     }
 125 
 126     return result;
 127 }
 128 
 129 int CJavaInputStreamCallbacks::ReadBlock(int64_t position, int size)
 130 {
 131     int result = -1;
 132     CJavaEnvironment javaEnv(m_jvm);
 133     JNIEnv *pEnv = javaEnv.getEnvironment();
 134 
 135     if (m_ConnectionHolder && pEnv)
 136     {
 137         result = pEnv->CallIntMethod(m_ConnectionHolder, m_ReadBlockMID, position, size);
 138         if (javaEnv.clearException()) {
 139             result = -2;
 140         }
 141     }
 142 
 143     return result;
 144 }
 145 
 146 void CJavaInputStreamCallbacks::CopyBlock(void* destination, int size)
 147 {
 148     CJavaEnvironment javaEnv(m_jvm);
 149     JNIEnv *pEnv = javaEnv.getEnvironment();
 150     if (m_ConnectionHolder && pEnv)
 151     {
 152         jobject buffer = pEnv->GetObjectField(m_ConnectionHolder, m_BufferFID);
 153         void *data = pEnv->GetDirectBufferAddress(buffer);
 154 
 155         memcpy(destination, data, size);
 156         pEnv->DeleteLocalRef(buffer);
 157     }
 158  }
 159 
 160 bool CJavaInputStreamCallbacks::IsSeekable()
 161 {
 162     CJavaEnvironment javaEnv(m_jvm);
 163     JNIEnv *pEnv = javaEnv.getEnvironment();
 164     bool result = false;
 165 
 166     if (m_ConnectionHolder && pEnv)
 167     {
 168         result = (pEnv->CallBooleanMethod(m_ConnectionHolder, m_IsSeekableMID) == JNI_TRUE);
 169         javaEnv.reportException();
 170     }
 171 
 172     return result;
 173 }
 174 
 175 bool CJavaInputStreamCallbacks::IsRandomAccess()
 176 {
 177     CJavaEnvironment javaEnv(m_jvm);
 178     JNIEnv *pEnv = javaEnv.getEnvironment();
 179     bool result = false;
 180 
 181     if (m_ConnectionHolder && pEnv)
 182     {
 183         result = (pEnv->CallBooleanMethod(m_ConnectionHolder, m_IsRandomAccessMID) == JNI_TRUE);
 184         javaEnv.reportException();
 185     }
 186 
 187     return result;
 188 }
 189 
 190 int64_t CJavaInputStreamCallbacks::Seek(int64_t position)
 191 {
 192     CJavaEnvironment javaEnv(m_jvm);
 193     JNIEnv *pEnv = javaEnv.getEnvironment();
 194     jlong result = -1;
 195 
 196     if (m_ConnectionHolder && pEnv)
 197     {
 198         result = pEnv->CallLongMethod(m_ConnectionHolder, m_SeekMID, (jlong)position);
 199         javaEnv.reportException();
 200     }
 201 
 202     return (int64_t)result;
 203 }
 204 
 205 void CJavaInputStreamCallbacks::CloseConnection()
 206 {
 207     CJavaEnvironment javaEnv(m_jvm);
 208     JNIEnv *pEnv = javaEnv.getEnvironment();
 209     if (m_ConnectionHolder && pEnv)
 210     {
 211         pEnv->CallVoidMethod(m_ConnectionHolder, m_CloseConnectionMID);
 212         javaEnv.reportException();
 213         pEnv->DeleteGlobalRef(m_ConnectionHolder);
 214         m_ConnectionHolder = NULL;
 215     }
 216 }
 217 
 218 int CJavaInputStreamCallbacks::Property(int prop, int value)
 219 {
 220     CJavaEnvironment javaEnv(m_jvm);
 221     JNIEnv *pEnv = javaEnv.getEnvironment();
 222     int result = 0;
 223 
 224     if (m_ConnectionHolder && pEnv)
 225     {
 226         result = pEnv->CallIntMethod(m_ConnectionHolder, m_PropertyMID, (jint)prop, (jint)value);
 227         javaEnv.reportException();
 228     }
 229 
 230     return result;
 231 }
 232 
 233 int CJavaInputStreamCallbacks::GetStreamSize()
 234 {
 235     CJavaEnvironment javaEnv(m_jvm);
 236     JNIEnv *pEnv = javaEnv.getEnvironment();
 237     int result = 0;
 238 
 239     if (m_ConnectionHolder && pEnv)
 240     {
 241         result = pEnv->CallIntMethod(m_ConnectionHolder, m_GetStreamSizeMID);
 242         javaEnv.reportException();
 243     }
 244 
 245     return result;
 246 }