001: /*
002:
003: Derby - Class org.apache.derby.impl.store.raw.data.RFResource
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.store.raw.data;
023:
024: import org.apache.derby.iapi.reference.SQLState;
025:
026: import org.apache.derby.iapi.services.context.ContextService;
027: import org.apache.derby.iapi.services.context.ContextManager;
028: import org.apache.derby.iapi.services.daemon.Serviceable;
029: import org.apache.derby.iapi.services.sanity.SanityManager;
030: import org.apache.derby.iapi.error.StandardException;
031: import org.apache.derby.iapi.store.access.FileResource;
032: import org.apache.derby.iapi.store.raw.Transaction;
033: import org.apache.derby.iapi.store.access.AccessFactoryGlobals;
034: import org.apache.derby.iapi.store.access.DatabaseInstant;
035: import org.apache.derby.iapi.store.raw.xact.RawTransaction;
036:
037: import org.apache.derby.io.StorageFactory;
038: import org.apache.derby.io.WritableStorageFactory;
039: import org.apache.derby.io.StorageFile;
040: import org.apache.derby.io.StorageRandomAccessFile;
041:
042: import java.io.InputStream;
043: import java.io.OutputStream;
044: import java.io.FileNotFoundException;
045: import java.io.IOException;
046:
047: class RFResource implements FileResource {
048:
049: protected final BaseDataFileFactory factory;
050:
051: public RFResource(BaseDataFileFactory dataFactory) {
052: this .factory = dataFactory;
053: }
054:
055: /**
056: @see FileResource#add
057: @exception StandardException Oops
058: */
059: public long add(String name, InputStream source)
060: throws StandardException {
061: OutputStream os = null;
062:
063: if (factory.isReadOnly()) {
064: throw StandardException
065: .newException(SQLState.FILE_READ_ONLY);
066: }
067:
068: long generationId = factory.getNextId();
069:
070: try {
071: StorageFile file = getAsFile(name, generationId);
072: if (file.exists()) {
073: throw StandardException.newException(
074: SQLState.FILE_EXISTS, file);
075: }
076:
077: ContextManager cm = ContextService.getFactory()
078: .getCurrentContextManager();
079:
080: RawTransaction tran = factory.getRawStoreFactory()
081: .getXactFactory().findUserTransaction(
082: factory.getRawStoreFactory(), cm,
083: AccessFactoryGlobals.USER_TRANS_NAME);
084:
085: // Block the backup, If backup is already in progress wait
086: // for the backup to finish. Jar files are unlogged but the
087: // changes to the references to the jar file in the catalogs
088: // is logged. A consistent backup can not be made when jar file
089: // is being added.
090:
091: tran.blockBackup(true);
092:
093: StorageFile directory = file.getParentDir();
094: if (!directory.exists()) {
095: if (!directory.mkdirs()) {
096: throw StandardException.newException(
097: SQLState.FILE_CANNOT_CREATE_SEGMENT,
098: directory);
099: }
100: }
101:
102: os = file.getOutputStream();
103: byte[] data = new byte[4096];
104: int len;
105:
106: factory.writeInProgress();
107: try {
108: while ((len = source.read(data)) != -1) {
109: os.write(data, 0, len);
110: }
111: factory.writableStorageFactory.sync(os, false);
112: } finally {
113: factory.writeFinished();
114: }
115: }
116:
117: catch (IOException ioe) {
118: throw StandardException.newException(
119: SQLState.FILE_UNEXPECTED_EXCEPTION, ioe);
120: }
121:
122: finally {
123: try {
124: if (os != null) {
125: os.close();
126: }
127: } catch (IOException ioe2) {/*RESOLVE: Why ignore this?*/
128: }
129:
130: try {
131: if (source != null)
132: source.close();
133: } catch (IOException ioe2) {/* RESOLVE: Why ignore this?*/
134: }
135: }
136:
137: return generationId;
138: }
139:
140: /**
141: @see FileResource#remove
142: @exception StandardException Oops
143: */
144: public void remove(String name, long currentGenerationId,
145: boolean purgeOnCommit) throws StandardException {
146: if (factory.isReadOnly())
147: throw StandardException
148: .newException(SQLState.FILE_READ_ONLY);
149:
150: ContextManager cm = ContextService.getFactory()
151: .getCurrentContextManager();
152:
153: RawTransaction tran = factory.getRawStoreFactory()
154: .getXactFactory().findUserTransaction(
155: factory.getRawStoreFactory(), cm,
156: AccessFactoryGlobals.USER_TRANS_NAME);
157:
158: // Block the backup, If backup is already in progress wait
159: // for the backup to finish. Jar files are unlogged but the
160: // changes to the references to the jar file in the catalogs
161: // is logged. A consistent backup can not be made when jar file
162: // is being removed.
163:
164: tran.blockBackup(true);
165:
166: tran.logAndDo(new RemoveFileOperation(name,
167: currentGenerationId, purgeOnCommit));
168:
169: if (purgeOnCommit) {
170:
171: Serviceable s = new RemoveFile(getAsFile(name,
172: currentGenerationId));
173:
174: tran.addPostCommitWork(s);
175: }
176: }
177:
178: /**
179: @see FileResource#replace
180: @exception StandardException Oops
181: */
182: public long replace(String name, long currentGenerationId,
183: InputStream source, boolean purgeOnCommit)
184: throws StandardException {
185: if (factory.isReadOnly())
186: throw StandardException
187: .newException(SQLState.FILE_READ_ONLY);
188:
189: remove(name, currentGenerationId, purgeOnCommit);
190:
191: long generationId = add(name, source);
192:
193: return generationId;
194: }
195:
196: /**
197: @see FileResource#getAsFile
198: */
199: public StorageFile getAsFile(String name, long generationId) {
200: String versionedFileName = factory.getVersionedName(name,
201: generationId);
202:
203: return factory.storageFactory.newStorageFile(versionedFileName);
204: }
205:
206: /**
207: @see FileResource#getAsFile
208: */
209: private StorageFile getAsFile(String name) {
210: return factory.storageFactory.newStorageFile(name);
211: }
212:
213: /**
214: @see FileResource#getAsStream
215: @exception IOException trouble accessing file.
216: */
217: public InputStream getAsStream(String name, long generationId)
218: throws IOException {
219: return getAsFile(name, generationId).getInputStream();
220: }
221:
222: public char getSeparatorChar() {
223: return factory.storageFactory.getSeparator();
224: }
225: } // end of class RFResource
226:
227: class RemoveFile implements Serviceable {
228: private final StorageFile fileToGo;
229:
230: RemoveFile(StorageFile fileToGo) {
231: this .fileToGo = fileToGo;
232: }
233:
234: public int performWork(ContextManager context)
235: throws StandardException {
236: // SECURITY PERMISSION - MP1, OP5
237: if (fileToGo.exists()) {
238: if (!fileToGo.delete()) {
239: throw StandardException.newException(
240: SQLState.FILE_CANNOT_REMOVE_FILE, fileToGo);
241: }
242: }
243: return Serviceable.DONE;
244: }
245:
246: public boolean serviceASAP() {
247: return false;
248: }
249:
250: /**
251: * File deletion is a quick operation and typically releases substantial
252: * amount of space very quickly, this work should be done on the
253: * user thread.
254: * @return true, this work needs to done on user thread.
255: */
256: public boolean serviceImmediately() {
257: return true;
258: }
259: }
|