|
|||||||||||||||||||
| Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
| AbstractDiskPersistenceListener.java | 61.1% | 73% | 77.3% | 71.1% |
|
||||||||||||||
| 1 |
/*
|
|
| 2 |
* Copyright (c) 2002-2003 by OpenSymphony
|
|
| 3 |
* All rights reserved.
|
|
| 4 |
*/
|
|
| 5 |
package com.opensymphony.oscache.plugins.diskpersistence;
|
|
| 6 |
|
|
| 7 |
import com.opensymphony.oscache.base.Config;
|
|
| 8 |
import com.opensymphony.oscache.base.persistence.CachePersistenceException;
|
|
| 9 |
import com.opensymphony.oscache.base.persistence.PersistenceListener;
|
|
| 10 |
import com.opensymphony.oscache.web.ServletCacheAdministrator;
|
|
| 11 |
|
|
| 12 |
import org.apache.commons.logging.Log;
|
|
| 13 |
import org.apache.commons.logging.LogFactory;
|
|
| 14 |
|
|
| 15 |
import java.io.*;
|
|
| 16 |
|
|
| 17 |
import java.util.Set;
|
|
| 18 |
|
|
| 19 |
import javax.servlet.jsp.PageContext;
|
|
| 20 |
|
|
| 21 |
/**
|
|
| 22 |
* Persist the cache data to disk.
|
|
| 23 |
*
|
|
| 24 |
* The code in this class is totally not thread safe it is the resonsibility
|
|
| 25 |
* of the cache using this persistence listener to handle the concurrency.
|
|
| 26 |
*
|
|
| 27 |
* @author <a href="mailto:fbeauregard@pyxis-tech.com">Francois Beauregard</a>
|
|
| 28 |
* @author <a href="mailto:abergevin@pyxis-tech.com">Alain Bergevin</a>
|
|
| 29 |
* @author <a href="mailto:chris@swebtec.com">Chris Miller</a>
|
|
| 30 |
* @author <a href="mailto:amarch@soe.sony.com">Andres March</a>
|
|
| 31 |
*/
|
|
| 32 |
public abstract class AbstractDiskPersistenceListener implements PersistenceListener, Serializable { |
|
| 33 |
protected final static String CACHE_PATH_KEY = "cache.path"; |
|
| 34 |
|
|
| 35 |
/**
|
|
| 36 |
* File extension for disk cache file
|
|
| 37 |
*/
|
|
| 38 |
protected final static String CACHE_EXTENSION = "cache"; |
|
| 39 |
|
|
| 40 |
/**
|
|
| 41 |
* The directory that cache groups are stored under
|
|
| 42 |
*/
|
|
| 43 |
protected final static String GROUP_DIRECTORY = "__groups__"; |
|
| 44 |
|
|
| 45 |
/**
|
|
| 46 |
* Sub path name for application cache
|
|
| 47 |
*/
|
|
| 48 |
protected final static String APPLICATION_CACHE_SUBPATH = "application"; |
|
| 49 |
|
|
| 50 |
/**
|
|
| 51 |
* Sub path name for session cache
|
|
| 52 |
*/
|
|
| 53 |
protected final static String SESSION_CACHE_SUBPATH = "session"; |
|
| 54 |
|
|
| 55 |
/**
|
|
| 56 |
* Property to get the temporary working directory of the servlet container.
|
|
| 57 |
*/
|
|
| 58 |
protected static final String CONTEXT_TMPDIR = "javax.servlet.context.tempdir"; |
|
| 59 |
private static transient final Log log = LogFactory.getLog(AbstractDiskPersistenceListener.class); |
|
| 60 |
|
|
| 61 |
/**
|
|
| 62 |
* Base path where the disk cache reside.
|
|
| 63 |
*/
|
|
| 64 |
private File cachePath = null; |
|
| 65 |
private File contextTmpDir;
|
|
| 66 |
|
|
| 67 |
/**
|
|
| 68 |
* Root path for disk cache
|
|
| 69 |
*/
|
|
| 70 |
private String root = null; |
|
| 71 |
|
|
| 72 |
/**
|
|
| 73 |
* Get the physical cache path on disk.
|
|
| 74 |
*
|
|
| 75 |
* @return A file representing the physical cache location.
|
|
| 76 |
*/
|
|
| 77 | 75 |
public File getCachePath() {
|
| 78 | 75 |
return cachePath;
|
| 79 |
} |
|
| 80 |
|
|
| 81 |
/**
|
|
| 82 |
* Get the root directory for persisting the cache on disk.
|
|
| 83 |
* This path includes scope and sessionId, if any.
|
|
| 84 |
*
|
|
| 85 |
* @return A String representing the root directory.
|
|
| 86 |
*/
|
|
| 87 | 0 |
public String getRoot() {
|
| 88 | 0 |
return root;
|
| 89 |
} |
|
| 90 |
|
|
| 91 |
/**
|
|
| 92 |
* Get the servlet context tmp directory.
|
|
| 93 |
*
|
|
| 94 |
* @return A file representing the servlet context tmp directory.
|
|
| 95 |
*/
|
|
| 96 | 0 |
public File getContextTmpDir() {
|
| 97 | 0 |
return contextTmpDir;
|
| 98 |
} |
|
| 99 |
|
|
| 100 |
/**
|
|
| 101 |
* Verify if a group exists in the cache
|
|
| 102 |
*
|
|
| 103 |
* @param group The group name to check
|
|
| 104 |
* @return True if it exists
|
|
| 105 |
* @throws CachePersistenceException
|
|
| 106 |
*/
|
|
| 107 | 0 |
public boolean isGroupStored(String group) throws CachePersistenceException { |
| 108 | 0 |
try {
|
| 109 | 0 |
File file = getCacheGroupFile(group); |
| 110 |
|
|
| 111 | 0 |
return file.exists();
|
| 112 |
} catch (Exception e) {
|
|
| 113 | 0 |
throw new CachePersistenceException("Unable verify group '" + group + "' exists in the cache: " + e); |
| 114 |
} |
|
| 115 |
} |
|
| 116 |
|
|
| 117 |
/**
|
|
| 118 |
* Verify if an object is currently stored in the cache
|
|
| 119 |
*
|
|
| 120 |
* @param key The object key
|
|
| 121 |
* @return True if it exists
|
|
| 122 |
* @throws CachePersistenceException
|
|
| 123 |
*/
|
|
| 124 | 64 |
public boolean isStored(String key) throws CachePersistenceException { |
| 125 | 64 |
try {
|
| 126 | 64 |
File file = getCacheFile(key); |
| 127 |
|
|
| 128 | 64 |
return file.exists();
|
| 129 |
} catch (Exception e) {
|
|
| 130 | 0 |
throw new CachePersistenceException("Unable verify id '" + key + "' is stored in the cache: " + e); |
| 131 |
} |
|
| 132 |
} |
|
| 133 |
|
|
| 134 |
/**
|
|
| 135 |
* Clears the whole cache directory, starting from the root
|
|
| 136 |
*
|
|
| 137 |
* @throws CachePersistenceException
|
|
| 138 |
*/
|
|
| 139 | 73 |
public void clear() throws CachePersistenceException { |
| 140 | 73 |
clear(root); |
| 141 |
} |
|
| 142 |
|
|
| 143 |
/**
|
|
| 144 |
* Initialises this <tt>DiskPersistenceListener</tt> using the supplied
|
|
| 145 |
* configuration.
|
|
| 146 |
*
|
|
| 147 |
* @param config The OSCache configuration
|
|
| 148 |
*/
|
|
| 149 | 75 |
public PersistenceListener configure(Config config) {
|
| 150 | 75 |
String sessionId = null;
|
| 151 | 75 |
int scope = 0;
|
| 152 | 75 |
initFileCaching(config.getProperty(CACHE_PATH_KEY)); |
| 153 |
|
|
| 154 | 75 |
if (config.getProperty(ServletCacheAdministrator.HASH_KEY_SESSION_ID) != null) { |
| 155 | 0 |
sessionId = config.getProperty(ServletCacheAdministrator.HASH_KEY_SESSION_ID); |
| 156 |
} |
|
| 157 |
|
|
| 158 | 75 |
if (config.getProperty(ServletCacheAdministrator.HASH_KEY_SCOPE) != null) { |
| 159 | 0 |
scope = Integer.parseInt(config.getProperty(ServletCacheAdministrator.HASH_KEY_SCOPE)); |
| 160 |
} |
|
| 161 |
|
|
| 162 | 75 |
StringBuffer root = new StringBuffer(getCachePath().getPath());
|
| 163 | 75 |
root.append("/");
|
| 164 | 75 |
root.append(getPathPart(scope)); |
| 165 |
|
|
| 166 | 75 |
if ((sessionId != null) && (sessionId.length() > 0)) { |
| 167 | 0 |
root.append("/");
|
| 168 | 0 |
root.append(sessionId); |
| 169 |
} |
|
| 170 |
|
|
| 171 | 75 |
this.root = root.toString();
|
| 172 | 75 |
this.contextTmpDir = (File) config.get(ServletCacheAdministrator.HASH_KEY_CONTEXT_TMPDIR);
|
| 173 |
|
|
| 174 | 75 |
return this; |
| 175 |
} |
|
| 176 |
|
|
| 177 |
/**
|
|
| 178 |
* Delete a single cache entry.
|
|
| 179 |
*
|
|
| 180 |
* @param key The object key to delete
|
|
| 181 |
* @throws CachePersistenceException
|
|
| 182 |
*/
|
|
| 183 | 21 |
public void remove(String key) throws CachePersistenceException { |
| 184 | 21 |
File file = getCacheFile(key); |
| 185 | 21 |
remove(file); |
| 186 |
} |
|
| 187 |
|
|
| 188 |
/**
|
|
| 189 |
* Deletes an entire group from the cache.
|
|
| 190 |
*
|
|
| 191 |
* @param groupName The name of the group to delete
|
|
| 192 |
* @throws CachePersistenceException
|
|
| 193 |
*/
|
|
| 194 | 0 |
public void removeGroup(String groupName) throws CachePersistenceException { |
| 195 | 0 |
File file = getCacheGroupFile(groupName); |
| 196 | 0 |
remove(file); |
| 197 |
} |
|
| 198 |
|
|
| 199 |
/**
|
|
| 200 |
* Retrieve an object from the disk
|
|
| 201 |
*
|
|
| 202 |
* @param key The object key
|
|
| 203 |
* @return The retrieved object
|
|
| 204 |
* @throws CachePersistenceException
|
|
| 205 |
*/
|
|
| 206 | 372 |
public Object retrieve(String key) throws CachePersistenceException { |
| 207 | 372 |
return retrieve(getCacheFile(key));
|
| 208 |
} |
|
| 209 |
|
|
| 210 |
/**
|
|
| 211 |
* Retrieves a group from the cache, or <code>null</code> if the group
|
|
| 212 |
* file could not be found.
|
|
| 213 |
*
|
|
| 214 |
* @param groupName The name of the group to retrieve.
|
|
| 215 |
* @return A <code>Set</code> containing keys of all of the cache
|
|
| 216 |
* entries that belong to this group.
|
|
| 217 |
* @throws CachePersistenceException
|
|
| 218 |
*/
|
|
| 219 | 109 |
public Set retrieveGroup(String groupName) throws CachePersistenceException { |
| 220 | 109 |
File groupFile = getCacheGroupFile(groupName); |
| 221 |
|
|
| 222 | 109 |
try {
|
| 223 | 109 |
return (Set) retrieve(groupFile);
|
| 224 |
} catch (ClassCastException e) {
|
|
| 225 | 0 |
throw new CachePersistenceException("Group file " + groupFile + " was not persisted as a Set: " + e); |
| 226 |
} |
|
| 227 |
} |
|
| 228 |
|
|
| 229 |
/**
|
|
| 230 |
* Stores an object in cache
|
|
| 231 |
*
|
|
| 232 |
* @param key The object's key
|
|
| 233 |
* @param obj The object to store
|
|
| 234 |
* @throws CachePersistenceException
|
|
| 235 |
*/
|
|
| 236 | 182 |
public void store(String key, Object obj) throws CachePersistenceException { |
| 237 | 182 |
File file = getCacheFile(key); |
| 238 | 182 |
store(file, obj); |
| 239 |
} |
|
| 240 |
|
|
| 241 |
/**
|
|
| 242 |
* Stores a group in the persistent cache. This will overwrite any existing
|
|
| 243 |
* group with the same name
|
|
| 244 |
*/
|
|
| 245 | 98 |
public void storeGroup(String groupName, Set group) throws CachePersistenceException { |
| 246 | 98 |
File groupFile = getCacheGroupFile(groupName); |
| 247 | 98 |
store(groupFile, group); |
| 248 |
} |
|
| 249 |
|
|
| 250 |
/**
|
|
| 251 |
* Allows to translate to the temp dir of the servlet container if cachePathStr
|
|
| 252 |
* is javax.servlet.context.tempdir.
|
|
| 253 |
*
|
|
| 254 |
* @param cachePathStr Cache path read from the properties file.
|
|
| 255 |
* @return Adjusted cache path
|
|
| 256 |
*/
|
|
| 257 | 0 |
protected String adjustFileCachePath(String cachePathStr) {
|
| 258 | 0 |
if (cachePathStr.compareToIgnoreCase(CONTEXT_TMPDIR) == 0) {
|
| 259 | 0 |
cachePathStr = contextTmpDir.getAbsolutePath(); |
| 260 |
} |
|
| 261 |
|
|
| 262 | 0 |
return cachePathStr;
|
| 263 |
} |
|
| 264 |
|
|
| 265 |
/**
|
|
| 266 |
* Set caching to file on or off.
|
|
| 267 |
* If the <code>cache.path</code> property exists, we assume file caching is turned on.
|
|
| 268 |
* By the same token, to turn off file caching just remove this property.
|
|
| 269 |
*/
|
|
| 270 | 75 |
protected void initFileCaching(String cachePathStr) { |
| 271 | 75 |
if (cachePathStr != null) { |
| 272 | 75 |
cachePath = new File(cachePathStr);
|
| 273 |
|
|
| 274 | 75 |
try {
|
| 275 | 75 |
if (!cachePath.exists()) {
|
| 276 | 4 |
if (log.isInfoEnabled()) {
|
| 277 | 4 |
log.info("cache.path '" + cachePathStr + "' does not exist, creating"); |
| 278 |
} |
|
| 279 |
|
|
| 280 | 4 |
cachePath.mkdirs(); |
| 281 |
} |
|
| 282 |
|
|
| 283 | 75 |
if (!cachePath.isDirectory()) {
|
| 284 | 0 |
log.error("cache.path '" + cachePathStr + "' is not a directory"); |
| 285 | 0 |
cachePath = null;
|
| 286 | 75 |
} else if (!cachePath.canWrite()) { |
| 287 | 0 |
log.error("cache.path '" + cachePathStr + "' is not a writable location"); |
| 288 | 0 |
cachePath = null;
|
| 289 |
} |
|
| 290 |
} catch (Exception e) {
|
|
| 291 | 0 |
log.error("cache.path '" + cachePathStr + "' could not be used", e); |
| 292 | 0 |
cachePath = null;
|
| 293 |
} |
|
| 294 |
} else {
|
|
| 295 |
// Use default value
|
|
| 296 |
} |
|
| 297 |
} |
|
| 298 |
|
|
| 299 | 21 |
protected void remove(File file) throws CachePersistenceException { |
| 300 | 21 |
try {
|
| 301 |
// Loop until we are able to delete (No current read).
|
|
| 302 |
// The cache must ensure that there are never two concurrent threads
|
|
| 303 |
// doing write (store and delete) operations on the same item.
|
|
| 304 |
// Delete only should be enough but file.exists prevents infinite loop
|
|
| 305 | 21 |
while (!file.delete() && file.exists()) {
|
| 306 |
; |
|
| 307 |
} |
|
| 308 |
} catch (Exception e) {
|
|
| 309 | 0 |
throw new CachePersistenceException("Unable to remove '" + file + "' from the cache: " + e); |
| 310 |
} |
|
| 311 |
} |
|
| 312 |
|
|
| 313 |
/**
|
|
| 314 |
* Stores an object using the supplied file object
|
|
| 315 |
*
|
|
| 316 |
* @param file The file to use for storing the object
|
|
| 317 |
* @param obj the object to store
|
|
| 318 |
* @throws CachePersistenceException
|
|
| 319 |
*/
|
|
| 320 | 280 |
protected void store(File file, Object obj) throws CachePersistenceException { |
| 321 |
// check if the directory structure required exists and create it if it doesn't
|
|
| 322 | 280 |
File filepath = new File(file.getParent());
|
| 323 |
|
|
| 324 | 280 |
try {
|
| 325 | 280 |
if (!filepath.exists()) {
|
| 326 | 27 |
filepath.mkdirs(); |
| 327 |
} |
|
| 328 |
} catch (Exception e) {
|
|
| 329 | 0 |
throw new CachePersistenceException("Unable to create the directory " + filepath); |
| 330 |
} |
|
| 331 |
|
|
| 332 |
// Loop until we are able to delete (No current read).
|
|
| 333 |
// The cache must ensure that there are never two concurrent threads
|
|
| 334 |
// doing write (store and delete) operations on the same item.
|
|
| 335 |
// Delete only should be enough but file.exists prevents infinite loop
|
|
| 336 | 280 |
while (file.exists() && !file.delete()) {
|
| 337 |
; |
|
| 338 |
} |
|
| 339 |
|
|
| 340 |
// Write the object to disk
|
|
| 341 | 280 |
FileOutputStream fout = null;
|
| 342 | 280 |
ObjectOutputStream oout = null;
|
| 343 |
|
|
| 344 | 280 |
try {
|
| 345 | 280 |
fout = new FileOutputStream(file);
|
| 346 | 280 |
oout = new ObjectOutputStream(fout);
|
| 347 | 280 |
oout.writeObject(obj); |
| 348 | 280 |
oout.flush(); |
| 349 |
} catch (Exception e) {
|
|
| 350 | 0 |
throw new CachePersistenceException("Unable to write '" + file + "' in the cache. Exception: " + e.getClass().getName() + ", Message: " + e.getMessage()); |
| 351 |
} finally {
|
|
| 352 | 280 |
try {
|
| 353 | 280 |
fout.close(); |
| 354 |
} catch (Exception e) {
|
|
| 355 |
} |
|
| 356 |
|
|
| 357 | 280 |
try {
|
| 358 | 280 |
oout.close(); |
| 359 |
} catch (Exception e) {
|
|
| 360 |
} |
|
| 361 |
} |
|
| 362 |
} |
|
| 363 |
|
|
| 364 |
/**
|
|
| 365 |
* Build fully qualified cache file for the specified cache entry key.
|
|
| 366 |
*
|
|
| 367 |
* @param key Cache Entry Key.
|
|
| 368 |
* @return File reference.
|
|
| 369 |
*/
|
|
| 370 | 639 |
protected File getCacheFile(String key) {
|
| 371 | 639 |
char[] fileChars = getCacheFileName(key);
|
| 372 |
|
|
| 373 | 639 |
File file = new File(root, new String(fileChars) + "." + CACHE_EXTENSION); |
| 374 |
|
|
| 375 | 639 |
return file;
|
| 376 |
} |
|
| 377 |
|
|
| 378 |
/**
|
|
| 379 |
* Build cache file name for the specified cache entry key.
|
|
| 380 |
*
|
|
| 381 |
* @param key Cache Entry Key.
|
|
| 382 |
* @return char[] file name.
|
|
| 383 |
*/
|
|
| 384 |
protected abstract char[] getCacheFileName(String key); |
|
| 385 |
|
|
| 386 |
/**
|
|
| 387 |
* Builds a fully qualified file name that specifies a cache group entry.
|
|
| 388 |
*
|
|
| 389 |
* @param group The name of the group
|
|
| 390 |
* @return A File reference
|
|
| 391 |
*/
|
|
| 392 | 207 |
private File getCacheGroupFile(String group) {
|
| 393 | 207 |
int AVERAGE_PATH_LENGTH = 30;
|
| 394 |
|
|
| 395 | 207 |
if ((group == null) || (group.length() == 0)) { |
| 396 | 0 |
throw new IllegalArgumentException("Invalid group '" + group + "' specified to getCacheGroupFile."); |
| 397 |
} |
|
| 398 |
|
|
| 399 | 207 |
StringBuffer path = new StringBuffer(AVERAGE_PATH_LENGTH);
|
| 400 |
|
|
| 401 |
// Build a fully qualified file name for this group
|
|
| 402 | 207 |
path.append(GROUP_DIRECTORY).append('/');
|
| 403 | 207 |
path.append(group).append('.').append(CACHE_EXTENSION);
|
| 404 |
|
|
| 405 | 207 |
return new File(root, path.toString()); |
| 406 |
} |
|
| 407 |
|
|
| 408 |
/**
|
|
| 409 |
* This allows to persist different scopes in different path in the case of
|
|
| 410 |
* file caching.
|
|
| 411 |
*
|
|
| 412 |
* @param scope Cache scope.
|
|
| 413 |
* @return The scope subpath
|
|
| 414 |
*/
|
|
| 415 | 75 |
private String getPathPart(int scope) { |
| 416 | 75 |
if (scope == PageContext.SESSION_SCOPE) {
|
| 417 | 0 |
return SESSION_CACHE_SUBPATH;
|
| 418 |
} else {
|
|
| 419 | 75 |
return APPLICATION_CACHE_SUBPATH;
|
| 420 |
} |
|
| 421 |
} |
|
| 422 |
|
|
| 423 |
/**
|
|
| 424 |
* Clears a whole directory, starting from the specified
|
|
| 425 |
* directory
|
|
| 426 |
*
|
|
| 427 |
* @param baseDirName The root directory to delete
|
|
| 428 |
* @throws CachePersistenceException
|
|
| 429 |
*/
|
|
| 430 | 73 |
private void clear(String baseDirName) throws CachePersistenceException { |
| 431 | 73 |
File baseDir = new File(baseDirName);
|
| 432 | 73 |
File[] fileList = baseDir.listFiles(); |
| 433 |
|
|
| 434 | 73 |
try {
|
| 435 | 73 |
if (fileList != null) { |
| 436 |
// Loop through all the files and directory to delete them
|
|
| 437 | 22 |
for (int count = 0; count < fileList.length; count++) { |
| 438 | 22 |
if (fileList[count].isFile()) {
|
| 439 | 22 |
fileList[count].delete(); |
| 440 |
} else {
|
|
| 441 |
// Make a recursive call to delete the directory
|
|
| 442 | 0 |
clear(fileList[count].toString()); |
| 443 | 0 |
fileList[count].delete(); |
| 444 |
} |
|
| 445 |
} |
|
| 446 |
} |
|
| 447 |
|
|
| 448 |
// Delete the root directory
|
|
| 449 | 73 |
baseDir.delete(); |
| 450 |
} catch (Exception e) {
|
|
| 451 | 0 |
throw new CachePersistenceException("Unable to clear the cache directory"); |
| 452 |
} |
|
| 453 |
} |
|
| 454 |
|
|
| 455 |
/**
|
|
| 456 |
* Retrives a serialized object from the supplied file, or returns
|
|
| 457 |
* <code>null</code> if the file does not exist.
|
|
| 458 |
*
|
|
| 459 |
* @param file The file to deserialize
|
|
| 460 |
* @return The deserialized object
|
|
| 461 |
* @throws CachePersistenceException
|
|
| 462 |
*/
|
|
| 463 | 481 |
private Object retrieve(File file) throws CachePersistenceException { |
| 464 | 481 |
Object readContent = null;
|
| 465 | 481 |
boolean fileExist;
|
| 466 |
|
|
| 467 | 481 |
try {
|
| 468 | 481 |
fileExist = file.exists(); |
| 469 |
} catch (Exception e) {
|
|
| 470 | 0 |
throw new CachePersistenceException("Unable to verify if " + file + " exists: " + e); |
| 471 |
} |
|
| 472 |
|
|
| 473 |
// Read the file if it exists
|
|
| 474 | 481 |
if (fileExist) {
|
| 475 | 269 |
BufferedInputStream in = null;
|
| 476 | 269 |
ObjectInputStream oin = null;
|
| 477 |
|
|
| 478 | 269 |
try {
|
| 479 | 269 |
in = new BufferedInputStream(new FileInputStream(file)); |
| 480 | 269 |
oin = new ObjectInputStream(in);
|
| 481 | 269 |
readContent = oin.readObject(); |
| 482 |
} catch (Exception e) {
|
|
| 483 |
// We expect this exception to occur.
|
|
| 484 |
// This is when the item will be invalidated (written or deleted)
|
|
| 485 |
// during read.
|
|
| 486 |
// The cache has the logic to retry reading.
|
|
| 487 | 0 |
throw new CachePersistenceException("Unable to read '" + file.getAbsolutePath() + "' from the cache: " + e); |
| 488 |
} finally {
|
|
| 489 | 269 |
try {
|
| 490 | 269 |
oin.close(); |
| 491 |
} catch (Exception ex) {
|
|
| 492 |
} |
|
| 493 |
|
|
| 494 | 269 |
try {
|
| 495 | 269 |
in.close(); |
| 496 |
} catch (Exception ex) {
|
|
| 497 |
} |
|
| 498 |
} |
|
| 499 |
} |
|
| 500 |
|
|
| 501 | 481 |
return readContent;
|
| 502 |
} |
|
| 503 |
} |
|
| 504 |
|
|
||||||||||