1 /**
2  * @copyright
3  * ====================================================================
4  *    Licensed to the Apache Software Foundation (ASF) under one
5  *    or more contributor license agreements.  See the NOTICE file
6  *    distributed with this work for additional information
7  *    regarding copyright ownership.  The ASF licenses this file
8  *    to you under the Apache License, Version 2.0 (the
9  *    "License"); you may not use this file except in compliance
10  *    with the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *    Unless required by applicable law or agreed to in writing,
15  *    software distributed under the License is distributed on an
16  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  *    KIND, either express or implied.  See the License for the
18  *    specific language governing permissions and limitations
19  *    under the License.
20  * ====================================================================
21  * @endcopyright
22  *
23  * @file InputStream.cpp
24  * @brief Implementation of the class InputStream
25  */
26 
27 #include "InputStream.h"
28 #include "JNIUtil.h"
29 #include "JNIByteArray.h"
30 
31 /**
32  * Create an InputStream object.
33  * @param jthis the Java object to be stored
34  */
InputStream(jobject jthis)35 InputStream::InputStream(jobject jthis)
36 {
37   m_jthis = jthis;
38 }
39 
~InputStream()40 InputStream::~InputStream()
41 {
42   // The m_jthis does not need to be destroyed, because it is the
43   // passed in parameter to the Java method.
44 }
45 
46 /**
47  * Create a svn_stream_t structure for this object. This will be used
48  * as an input stream by Subversion.
49  * @param pool  the pool, from which the structure is allocated
50  * @return the input stream
51  */
getStream(const SVN::Pool & pool)52 svn_stream_t *InputStream::getStream(const SVN::Pool &pool)
53 {
54   // Create a stream with this as the baton and set the read and
55   // close functions.
56   svn_stream_t *ret = svn_stream_create(this, pool.getPool());
57   svn_stream_set_read2(ret, InputStream::read,
58                        NULL /* only partial read support */);
59   svn_stream_set_close(ret, InputStream::close);
60   return ret;
61 }
62 
63 /**
64  * Implements svn_read_fn_t to read to data into Subversion.
65  * @param baton     an InputStream object for the callback
66  * @param buffer    the buffer for the read data
67  * @param len       on input the buffer len, on output the number of read bytes
68  * @return a subversion error or SVN_NO_ERROR
69  */
read(void * baton,char * buffer,apr_size_t * len)70 svn_error_t *InputStream::read(void *baton, char *buffer, apr_size_t *len)
71 {
72   if (0 == *len)
73     return SVN_NO_ERROR;
74 
75   JNIEnv *env = JNIUtil::getEnv();
76   // An object of our class is passed in as the baton.
77   InputStream *that = static_cast<InputStream *>(baton);
78 
79   // The method id will not change during the time this library is
80   // loaded, so it can be cached.
81   static jmethodID mid = 0;
82   if (mid == 0)
83     {
84       jclass clazz = env->FindClass("java/io/InputStream");
85       if (JNIUtil::isJavaExceptionThrown())
86         return SVN_NO_ERROR;
87 
88       mid = env->GetMethodID(clazz, "read", "([B)I");
89       if (JNIUtil::isJavaExceptionThrown() || mid == 0)
90         return SVN_NO_ERROR;
91 
92       env->DeleteLocalRef(clazz);
93     }
94 
95   // Allocate a Java byte array to read the data.
96   jbyteArray data = JNIUtil::makeJByteArray(buffer, static_cast<int>(*len));
97   if (JNIUtil::isJavaExceptionThrown())
98     return SVN_NO_ERROR;
99 
100   // Read the data.
101   jint jread = env->CallIntMethod(that->m_jthis, mid, data);
102   if (JNIUtil::isJavaExceptionThrown())
103     return SVN_NO_ERROR;
104 
105   /*
106    * Convert -1 from InputStream.read that means EOF, 0 which is subversion equivalent
107    */
108   if(jread == -1)
109     {
110       jread = 0;
111     }
112 
113   // Put the Java byte array into a helper object to retrieve the
114   // data bytes.
115   JNIByteArray outdata(data, true);
116   if (JNIUtil::isJavaExceptionThrown())
117     return SVN_NO_ERROR;
118 
119   // Catch when the Java method tells us it read too much data.
120   if (jread > (jint) *len)
121     jread = 0;
122 
123   // In the case of success copy the data back to the Subversion
124   // buffer.
125   if (jread > 0)
126     memcpy(buffer, outdata.getBytes(), jread);
127 
128   // Copy the number of read bytes back to Subversion.
129   *len = jread;
130 
131   return SVN_NO_ERROR;
132 }
133 
134 /**
135  * Implements svn_close_fn_t to close the input stream.
136  * @param baton     an InputStream object for the callback
137  * @return a subversion error or SVN_NO_ERROR
138  */
close(void * baton)139 svn_error_t *InputStream::close(void *baton)
140 {
141   JNIEnv *env = JNIUtil::getEnv();
142 
143   // An object of our class is passed in as the baton
144   InputStream *that = reinterpret_cast<InputStream*>(baton);
145 
146   // The method id will not change during the time this library is
147   // loaded, so it can be cached.
148   static jmethodID mid = 0;
149   if (mid == 0)
150     {
151       jclass clazz = env->FindClass("java/io/InputStream");
152       if (JNIUtil::isJavaExceptionThrown())
153         return SVN_NO_ERROR;
154 
155       mid = env->GetMethodID(clazz, "close", "()V");
156       if (JNIUtil::isJavaExceptionThrown() || mid == 0)
157         return SVN_NO_ERROR;
158 
159       env->DeleteLocalRef(clazz);
160     }
161 
162   // Call the Java object, to close the stream.
163   env->CallVoidMethod(that->m_jthis, mid);
164   // We don't need to check for an exception here because we return anyway.
165 
166   return SVN_NO_ERROR;
167 }
168