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