📄 contentstorage.java
字号:
* Do the work of adding the content to the storage. Calls the method * {@link #ensurePrealocating} before adding the content. * * @param content * @param safeMode * * @throws Exception if it was not stored whole content. */ private void addToContentEnsurePreallocating(String content, boolean safeMode) throws Exception { //checkSizeOfBodyPart(content.length()); ensurePrealocating(); long sizeAdded = 0; try { sizeAdded = addToContent(content, safeMode); } catch (Exception exception) { throw exception; } finally { handleAfterAdding(sizeAdded, Functions.getStringByteSize(content)); } } private void addToContentEnsurePreallocating(byte[] content, boolean safeMode) throws Exception { //checkSizeOfBodyPart(content.length); ensurePrealocating(); long sizeAdded = addToContentRaw(content, safeMode); handleAfterAdding(sizeAdded, content.length); } /** * Adds given byte array to the content of the bodypart. * If passed content cannot be saved tries to shorten the content and * save it partially. * * This method neither sets the size of the bodypart nor the state of the * bodypart. This is done by methods inside class <code>ContentStorage<code/> * that calls this method. * * This is a low-level method, saves bodypart always as a binary data regardless * of the body part header. More high level method is addToContent(String) * * Before calling this method, method {@link #ensurePrealocating} is always * called. * * @param content the content of the bodypart * @param safeMode if save mode is true, the content will be not saved * to new persistent storage, but to temporary one * @return the size of content that was added to the content. * content.length if all the content was written. */ protected abstract long addToContentRaw(byte[] content, boolean safeMode) throws Exception; /** * Gets given part of the content stored in this storage. First calling of * this method gets first part of the content. If there is another part, next * calling gets another part. If the returned part is the last, next returned * part will be first. * It can be determined whether next calling of this method will return first * part of the content by calling method willReturnFirstContent() * This method tries to get the part of maximum possible size. It loads the * content of this storage until it is reached the end of the storage or * it is not enough memory. Thats why the number of calling of getContent to * get whole content of this storage is typically less than number of calling * of method addToContent used to bufferContent the same content. * * @return given part of the content stored in this storage * * @throws MyException if the loading of the content was not successful */ public abstract String getNotRawContent() throws Throwable; /** * TODO: comment * @return * @throws Throwable */ public String getContent() throws Throwable { if (!isContentRaw()) { return getNotRawContent(); } else { return getContentRawAsString(); } } /** * Gets raw body part content as string that contains raw (not * encoded) content of body part content. */ private String getContentRawAsString() { if (DEBUG) System.out.println("DEBUG - ContentStorage.getContentRawAsString - is converted mode = " + bodyPart.convertedContentMode()); byte[] rawByteArray; try { rawByteArray = getContentRaw(); if (DEBUG) System.out.println("DEBUG - ContentStorage.getContentRawAsString - length of rawByteArray = " + rawByteArray.length); } catch (MyException ex) { ex.printStackTrace(); rawByteArray = new byte[0]; } return getStringWithRawContentFromByte(rawByteArray); } /** * Gets string from byte array with raw (not encoded) content of * this array. */ private String getStringWithRawContentFromByte(byte[] rawByteArray) { StringBuffer rawSB = new StringBuffer(rawByteArray.length); rawSB.setLength(rawByteArray.length); for (int i = 0; i < rawByteArray.length; i++) { rawSB.setCharAt(i, (char)rawByteArray[i]); } return rawSB.toString(); } /** * Sends content to given output connection. * @param connection the connection to that send the content. * @throws mujmail.MyException */ public void getContent(DataOutputStream outputStream) throws Throwable { do { if (isContentRaw()) { outputStream.write(getContentRaw()); } else { outputStream.write(getContent().getBytes("utf-8")); } } while (!willReturnFirstContent()); outputStream.flush(); } /** * Sends the content of this storage to given connection. * * @param connection the connection to that send the data. * @param sendingMode sending mode. * @param returnSendedData true if sent data should be returned. * @return the data sent to the connection if returnSendedData is true * "" if returnSendedData is false * @throws java.lang.Exception */ public abstract String sendContentToConnection(ConnectionInterface connection, MailSender.SendingModes sendingMode, boolean returnSendedData) throws Throwable; /** * * @return true if next calling of getContent or getContentRaw will return * first part of the content. */ public abstract boolean willReturnFirstContent(); /** * After calling this method, getContent or getContentRaw will return * first part of the content. */ public abstract void resetToFirstContent(); /** * Gets given part of the content stored in this storage. First calling of * this method gets first part of the content. If there is another part, next * calling gets another part. If the returned part is the last, next returned * part will be first. * It can be determined whether next calling of this method will return first * part of the content by calling method willReturnFirstContent() * This method tries to get the part of maximum possible size. It loads the * content of this storage until it is reached the end of the storage or * it is not enough memory. Thats why the number of calling of getContent to * get whole content of this storage is typically less than number of calling * of method addToContent used to bufferContent the same content. * * @return given part of the content stored in this storage represented as * binary data. * * @throws MyException if the loading of the content was not successful */ public abstract byte[] getContentRaw() throws MyException; /** * Gets the size of the content stored in this storage. * @return the size of content stored in this storage */ public long getSize() { return size; } /** * Sets the size of the content stored in this storage * @param size the new size of content stored in this storage */ public void setSize(long size) { this.size = size; } /** * Enumeration class which defines copying modes of the body part storage. * Used in copy constructor of body part storage. */ public static class CopyingModes { private CopyingModes() {}; /** Make deep copy. Original and new storages will store identical but independent content. */ public static final CopyingModes DEEP_COPY = new CopyingModes(); /** Makes shallow copy. Original and new storages will share this one content. Note, that this is allowed only if the body part which content is in the same box. */ public static final CopyingModes SHALLOW_COPY = new CopyingModes(); /** * Creates empty storage of the same type as original storage. */ public static final CopyingModes NO_COPY = new CopyingModes(); } /** * The enumeration of all possible storage types of body part. */ public static class StorageTypes { private static byte counter = 0; private final byte number; private final String name; private StorageTypes(String name) { number = counter++; this.name = name; } /** * Gets unique number identifying this storage type. * @return the number identifying this storage type */ public byte getStorageTypeNumber() { return number; } public String toString() { return name; } //#ifdef MUJMAIL_FS /** This storage uses filesystem to store the content of body part */ public static final StorageTypes FS_STORAGE = new StorageTypes("filesystem storage"); //#endif /** This storage uses rms database to store the content of body part */ public static final StorageTypes RMS_STORAGE = new StorageTypes("rms storage"); } /** * Sets correct state of bodypart after adding. If it was added less than it * should be added, deletes whole bodypart in the case that the bodypart is * not partially saveable. If it partially saveable, sets the state to partiall. * @param sizeWasAdded * @param sizeShouldBeAdded * @throws java.lang.Exception if sizeWasAdded < sizeShouldBeAdded */ private void handleAfterAdding(long sizeWasAdded, long sizeShouldBeAdded) throws Exception { if (sizeWasAdded == sizeShouldBeAdded) { getBodyPart().setBodyState(BodyPart.BS_COMPLETE); setSize(getSize() + sizeWasAdded); return; } if (!MessageHeader.canBePartiallySaved(getBodyPart())) { deleteContent(); throw new Exception("It was not possible to save whole content of bodypart. Bodypart deleted."); } getBodyPart().setBodyState(BodyPart.BS_PARTIAL); setSize(getSize() + sizeWasAdded); throw new Exception("It was not possible to save whole content of bodypart. Bodypart saved only partially."); } /** * Adds the data to the content. This means decodes it, saves it to appropriate * buffer and writes it when the memory is out or when flush method was called. * * The data can be decoded either to String or to byte[] so object of this * class contains one buffer that stores String data and flushes the content * to this ContentStorage using method addToContent(String, boolean) and one * buffer that stores byte[] data and flushes the content to this * ContentStorage using method addToContentRaw(byte[], boolean). */ private class BufferedContentAdder { /** Contains String data and flushes the output using addToContent. */ private AddToContentBuffer contentBuffer = new AddToContentBuffer(); /** Contains byte[] data and flushes the output using addToContentRaw. */ private AddToContentRawBuffer contentRawBuffer = new AddToContentRawBuffer(); /** * Decodes given string according to the encoding of the body part that * content this storage stores and stores it to appropriate buffer. * * @param bf the string which content store. */ public void bufferContent(String bf) throws Exception { try { if (isContentRaw()) { decodeAndBufferRawContent(bf); } else { decodeAndBufferContent(bf); } } catch (Exception e) { e.printStackTrace(); //throw new Exception(); //TODO: why to throw new _empty_ exception ? throw e; // rethrow } finally { } ensurePrealocating(); } /** * Clears the buffers cache. */ public void clearCache() { contentBuffer.clearCache(); contentRawBuffer.clearCache(); } /** * Forces the data to be written from buffers to this ContentStorage. * * @throws java.lang.Exception */ public void flush() throws Exception { System.out.println("Flushing buffers: "); if (contentBuffer.bufferSize() != 0) { contentBuffer.flush(); } if (contentRawBuffer.bufferSize() != 0) { contentRawBuffer.flush(); } } private void decodeAndBufferContent(String bf) throws Exception { String decoded = null; try { decoded = decodeNotRawBodypartData(bf); } catch (Exception exception) { flush(); System.gc(); decoded = decodeNotRawBodypartData(bf); } contentBuffer.write(decoded); } private void decodeAndBufferRawContent(String bf) throws Exception, Exception { byte[] decoded = null; try { if (bodyPart.convertedContentMode()) { decoded = bf.getBytes(); } else { decoded = Decode.decodeBase64(bf).toByteArray(); } } catch (Exception exception) { flush(); System.gc(); System.out.println("*** decoding bf = "+bf); decoded = Decode.decodeBase64(bf).toByteArray(); } contentRawBuffer.write(decoded); } } /** * StringOutpuBuffer that writes the data out from the buffer using method * addToContent. */ private class AddToContentBuffer extends OutputBuffer.StringOutputBuffer { protected void writeDataFromBuffer(String bufferData) throws Exception { addToContentEnsurePreallocating(bufferData, Settings.safeMode); } } /** * ByteOutpuBuffer that writes the data out from the buffer using method * addToContentRaw. */ private class AddToContentRawBuffer extends OutputBuffer.ByteOutputBuffer { protected void writeDataFromBuffer(byte[] bufferData) throws Exception { addToContentEnsurePreallocating(bufferData, Settings.safeMode); } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -